<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Maciej[cc] &#187; Programowanie</title><atom:link href="https://nabozny.io/category/programowanie/index.xml" rel="self" type="application/rss+xml"/><link>https://nabozny.io/category/programowanie/</link><description>Blog o programowaniu i nie tylko</description><lastBuildDate>Fri, 24 Sep 2021 08:32:30 +0000</lastBuildDate><language>pl-PL</language><generator>Hugo 0.167.0</generator><item><title>Debugowanie kontenerów w chmurze</title><link>https://nabozny.io/debugowanie-kontenerow-w-chmurze/</link><dc:creator>maciej</dc:creator><pubDate>Fri, 24 Sep 2021 08:29:21 +0000</pubDate><category>Cloud</category><category>Debugowanie</category><category>Linux</category><guid isPermaLink="true">https://nabozny.io/debugowanie-kontenerow-w-chmurze/</guid><description>Czasami coś nie działa i przyczyna tego za diabła nie chce dać się znaleźć. Poniżej kilka pomocnych rad w debugowaniu w środowisku chmury i kontenerów, czyli “u mnie działa, a na cudzym komputerze nie”</description><content:encoded>&lt;p&gt;Czasami coś nie działa i przyczyna tego za diabła nie chce dać się znaleźć. Poniżej kilka pomocnych rad w debugowaniu w środowisku chmury i kontenerów, czyli &amp;ldquo;u mnie działa, a na cudzym komputerze nie&amp;rdquo;&lt;/p&gt;&#10;&lt;h3 id="problem"&gt;Problem&lt;/h3&gt;&#10;&lt;p&gt;Moja strona nie działa, a devops skomplikował sprawę. Na wejściu do naszego serwisu stoi ELB, ALB lub inny load balancer, który przekierowuje ruch do kontenera zarządzanego przez ECS lub kubernetes. Żeby było trudniej, nie mamy dostępu do czeluści load balancera i wejście na noda klastra też jest ograniczone. Możemy jedynie dostać się do kontenera.&lt;/p&gt;&#10;&lt;p&gt;Porblem pojawia się gdy frontend (UI) wysyła żądanie do backendu. Nie dostajemy poprawnej odpowiedzi, a jedynie błąd 503 Service Unavailable.&lt;/p&gt;&#10;&lt;h3 id="rozwiązanie"&gt;Rozwiązanie&lt;/h3&gt;&#10;&lt;p&gt;Znajdź problem i napraw :)&lt;/p&gt;&#10;&lt;h3 id="rozwiązanie-2"&gt;Rozwiązanie 2&lt;/h3&gt;&#10;&lt;p&gt;Zanim zaczniesz grzebać, upewnij się, że zepsucie danego kontenera nie pociągnie za sobą negatywnych konsekwencji. W przypadku Kubernetesa, ECS lub innych orchestratorów (jakkolwiek to się tłumaczy) usunięcie zepsutego kontenera powinno spowodować automatyczne utworzenie nowego, takiego samego. Oprócz tego dobrą rzeczą będzie kawałek papieru lub gedit do notowania co robimy, aby łatwo dało się zrobić rollback zmian, które są trwałe lub odtworzyć całą ścieżkę.&lt;/p&gt;&#10;&lt;p&gt;Ponadto pomyśl czy żądanie/request, które będziemy naprawiać nie niszczy czegoś po stronie backendu. Jeśli problem sprawia guzik &amp;ldquo;skasuj konto&amp;rdquo; to dwukrotne debugowanie tego samego requestu będzie problematyczne&amp;hellip;&lt;/p&gt;&#10;&lt;h5 id="sprawdź-co-nie-bangla"&gt;Sprawdź co nie bangla&lt;/h5&gt;&#10;&lt;p&gt;Na początek znajdź z użyciem developer tools jakie żądanie sprawia problem. Otwórz &lt;em&gt;developer tools&lt;/em&gt; w przeglądarce i popatrz co się dzieje gdy strona wysyła do backendu request. W dalszej części bardzo pomocne będzie skopiowanie polecenia cURL z dokładnymi parametrami żądania, które wysyła przeglądarka. W zakładce z listą połączeń sieciowych z &lt;em&gt;developer tools&lt;/em&gt; możesz wybrać skopiowanie requestu m.in. jako polecenie programu cURL.&lt;/p&gt;&#10;&lt;p&gt;Ponadto, w ulubionym edytorze tekstu, jakim jest oczywiście &lt;em&gt;pico&lt;/em&gt; albo &lt;em&gt;gedit&lt;/em&gt;, zapisz powyższe polecenie. Często bedzie ono zawierało całą masę nagłówków, id sesji, ciasteczek i innych rzeczy, które weryfikuje backend w celu uwierzytelnienia człowieka. Jako użyteczny dodatek możesz zamienić w takim poleceniu nagłówek &lt;em&gt;User Agent&lt;/em&gt; na własny string. W większości logów user agent pojawia się w każdej linijce logowanego requestu i dzięki temu będzie nam łatwiej przeszukać logi tylko z naszego curla.&lt;/p&gt;&#10;&lt;p&gt;Mając powyższe sprawdź czy curl dostaje tak samo niepoprawną odpowiedź od serwera. Możliwe że już na tym etapie otrzymasz komunikat z błędem i dalsze debugowanie stanie się zbędne.&lt;/p&gt;&#10;&lt;h5 id="logi"&gt;Logi&lt;/h5&gt;&#10;&lt;p&gt;Chyba podstawowa rzecz, dużo grepa, a jeśli to AWS to dużo scrollowania i szukania w cloudwatch. Jeśli nie ma w logach aplikacji z backendu, to możliwe że jest w logach load balancera. Jeśli nie, to&amp;hellip;&lt;/p&gt;&#10;&lt;h3 id="czas-na-grubszy-kaliber"&gt;Czas na grubszy kaliber&lt;/h3&gt;&#10;&lt;p&gt;Czasami błąd jest tak uciążliwy, że ani w logach ani w odpowiedzi backendu nie da się znaleźć jego przyczyny. Pozostaje więc kilka cięższych dział:&lt;/p&gt;&#10;&lt;h5 id="wewnątrz-kontenera--netstat"&gt;Wewnątrz kontenera – netstat&lt;/h5&gt;&#10;&lt;p&gt;Odpal &lt;em&gt;netstat&lt;/em&gt; z pakietu &lt;em&gt;net-tools&lt;/em&gt; w Ubuntu/Debianie. Polecenie &lt;em&gt;netstat -ntap&lt;/em&gt; powinno wypisać wszystkie nawiązane połączenia i otwarte porty. Sprawdź czy wszystko jest tak jak powinno, tj. czy serwer nasłuchuje na odpowiednim porcie, czy widać połączenie z bazą etc.&lt;/p&gt;&#10;&lt;p&gt;Jeśli wszystko jest ok, to spróbuj wykonać to samo podczas wykonywania żądania przez cURL. Jeśli takie żądanie wykonuje się za szybko i nie nadążasz klikać, to wrzuć gdzieś &lt;em&gt;sleep 10&lt;/em&gt;, tylko nie zapomnij go usunąć :) Ponieważ często po zmodyfikowaniu kodu potrzebny jest restart serwisu, a ubicie głównego procesu spowoduje restart kontenera, spróbuj &lt;em&gt;kill -HUP [proces]&lt;/em&gt;. Dawno dawno temu, gdy komputery obsługiwało się bez myszki (i nie przez touch screen) zdarzało się, że coś zerwało połączenie z terminalem, na którym mieliśmy uruchomiony interaktywny proces. Przez &lt;em&gt;SIGHUP&lt;/em&gt; można było poinformować taką odłączoną aplikacje, że straciliśmy z nią połączenie i chcemy aby się zakończyła. Serwery zwykle nie reagowały na to, jednak przez lata &lt;em&gt;SIGHUP&lt;/em&gt; stał się sygnałem do reloadu konfiguracji i często również skryptów, które są wykonywane przez serwer. Dzięki temu prawdopodobnie nie zrestartujesz kontenera, w którym pracujesz.&lt;/p&gt;&#10;&lt;p&gt;Wracając do tematu – mając już wrzuconego gdzieś &lt;em&gt;sleep 10&lt;/em&gt; spróbuj netstat ponownie i zobacz czy nic nie wygląda podejrzanie&lt;/p&gt;&#10;&lt;h5 id="curl-do-kontenera"&gt;cURL do kontenera&lt;/h5&gt;&#10;&lt;p&gt;Kolejnym punktem do odhaczenia jest wysłanie żądania przez cURL bezpośrenio z kontenera do niego samego. Możemy wykorzystać wcześniejsze polecenie cURL. Możemy też dodać opcje wymuszającą na cURL&amp;rsquo;u wyświetlenie nagłówków. Jeśli się powiedzie, to problem prawdopodobnie pojawia się gdzieś wcześniej, np. między load balancerem a kontenerem. Jeśli masz taką możliwość, wykonaj to samo z load balancera.&lt;/p&gt;&#10;&lt;h5 id="nc-do-innych-usług"&gt;nc do innych usług&lt;/h5&gt;&#10;&lt;p&gt;Jeśli to zadziała, dobrze jest sprawdzić połączenie z bazą lub innymi wymaganymi serwisami bezpośrednio z kontenera: &lt;em&gt;nc nazwa_hosta port&lt;/em&gt; ( &lt;em&gt;apt install netcat&lt;/em&gt;). W przypadku bazy można doinstalować jej klienta i spróbować się połączyć, przy okazji wykonując jakiś prosty select na tabelach, do których aplikacja ma mieć dostęp.&lt;/p&gt;&#10;&lt;h5 id="udawanie-innych-serwisów"&gt;Udawanie innych serwisów&lt;/h5&gt;&#10;&lt;p&gt;Kolejnym trikiem, który może się przydać jest odpalenie czegoś, co udaje bazę lub inny serwis. Pozwoli to poznać nam jakie żądania są wysyłane do sąsiednich usług. Może to być przydatne gdy nie mamy możliwości zainstalowania &lt;em&gt;tcpdump&lt;/em&gt; lub jest to zabronione.&lt;/p&gt;&#10;&lt;p&gt;Jak to zrobić aby stało się to tylko lokalnie, bez psucia całej infrastruktury? Sprawdź jaki host jest ustawiony jako np. mikroserwis, który chcesz podsłuchać (jeśli używasz po IP, to sorry&amp;hellip;). Następnie wpisz w &lt;em&gt;/etc/hosts&lt;/em&gt; nowy wpis: &lt;em&gt;127.0.0.1 [nazwa hosta]&lt;/em&gt; i odpal w kontenerze &lt;em&gt;nc -l [port usługi]&lt;/em&gt;. W Linuksie pierwszeństwo w ustalaniu adresów przypisanych do hostów ma plik /etc/hosts, dzięki czemu dodając do niego nowy wpis wymusimy aby nasz kontener połączył się z localhost zamiast właściwego hosta. Uruchomiony program &lt;em&gt;nc&lt;/em&gt; pozwoli wypisać to, co miało zostać wysłane przez aplikację z naszego kontenera do serwisu. Pamiętaj, aby usunąć później wpis z &lt;em&gt;/etc/hosts&lt;/em&gt; :)&lt;/p&gt;&#10;&lt;h5 id="podglądnięcie-zmiennych-środowiskowych"&gt;Podglądnięcie zmiennych środowiskowych&lt;/h5&gt;&#10;&lt;p&gt;Często zanim połączymy się z bazą lub podglądniemy ruch sieciowy do danego hosta warto sprawdzić jakie zmienne znają aplikacje, które są już uruchomione w kontenerze. Czasami przez błąd w konfiguracji aplikacja może nie mieć możliwości uzyskania tych zmiennych, bo np. zabrakło uprawnienia IAM do odpytania Secret Parameter Store. Może też to wynikać ze zmienionego API lub biblioteki – zamiast jednego requestu o listę wszystkich parametrów odpytuje o każdy z osobna, do czego IAM już nie uprawnia.&lt;/p&gt;&#10;&lt;p&gt;Wykonaj &lt;em&gt;ps aux&lt;/em&gt;. Zwykle pierwszy proces będzie znał tylko te parametry, które są przekazywane bezpośrednio przez dockera. Wszelkie inne, uzyskiwane z chmury są znane późniejszym procesom, po pełnej inicjalizacji środowiska. Możesz podglądnąć jeden z wyższych PID&amp;rsquo;ów (ale nie z naszej sesji!) przez wpis w &lt;em&gt;/proc/[pid]/environ&lt;/em&gt;:&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;sudo cat /proc/123/environ | tr &amp;lsquo;\0&amp;rsquo; &amp;lsquo;\n&amp;rsquo;&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;Poszczególne zmienne są rozdzielone zerami (znakami o kodzie zero), stąd &lt;em&gt;tr&lt;/em&gt;, który zmieni zera na nowe linie. Jeśli wygląda ok, to pijemy kolejną kawę i szukamy dalej&lt;/p&gt;&#10;&lt;h5 id="ponownie-netstat-lsof"&gt;Ponownie netstat, lsof&lt;/h5&gt;&#10;&lt;p&gt;Sprawdź ulimit i porównaj z ilością połączeń i otwartych plików w kontenerze. W zależności od konfiguracji ten parametr przy dużej ilości połączeń może stwarzać problemy&lt;/p&gt;&#10;&lt;h5 id="podglądanie-z-czym-się-łączy-aplikacja"&gt;Podglądanie z czym się łączy aplikacja&lt;/h5&gt;&#10;&lt;p&gt;Sprawdzisz to przez &lt;em&gt;tcpdump udp port 53&lt;/em&gt;. W trakcie działania sniffera możesz wykonać żądanie przez cURL i sprawdzić co się dzieje i z czym się łączy aplikacja. Jeśli używasz IP, to zapytanie tcpdump będzie trochę inne.&lt;/p&gt;&#10;&lt;h5 id="strace-do-czytania-logów"&gt;strace do czytania logów&lt;/h5&gt;&#10;&lt;p&gt;Jeśli cloudwatch szczególnie uwiera tu i ówdzie, przez co nie możesz znaleźć logów, warto sprawdzić co aplikacja wypisuje na standardowe wyjście. Nightmare w kontenerze? Nie :) Spróbuj:&lt;br&gt;&#10;&lt;em&gt;strace --attach=[pid serwera] -e write=1,2&lt;/em&gt;&lt;br&gt;&#10;Powyższe podłączy &amp;ldquo;debuger&amp;rdquo; strace do procesu i będzie wyłapywało jedynie wywołania systemowe funkcji write do plików o fd=1 i 2. Oznaczają one odpowiednio &lt;em&gt;stdout&lt;/em&gt; i &lt;em&gt;stderr&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Nie jest to najwygodniejsza metoda oglądania logów, ale pozwala użyć ukochanego grepa i awk&amp;rsquo;a, co w przypadku cloudwatch nie zawsze jest najwygodniejsze i najszybsze.&lt;/p&gt;&#10;&lt;p&gt;Dla przykładowego &lt;em&gt;ls&lt;/em&gt; wykonanego przez ssh:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2021/09/strace-ssh.png" alt="" width="635" height="61" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;/p&gt;&#10;&lt;p&gt;otrzymamy wynik ze strace:&lt;br&gt;&#10;&lt;img src="https://nabozny.io/uploads/2021/09/strace-out.png" srcset="https://nabozny.io/uploads/2021/09/strace-out_hu_ff789cf743115bfc.png 600w, https://nabozny.io/uploads/2021/09/strace-out.png 1004w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="" width="1004" height="690" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;br&gt;&#10;Nie jest to najczytelniejsze, ale odpowiednio okraszając to grep&amp;rsquo;em i wycinając wszystkie znaki sterowania terminalem (kolory, znaki specjalne etc.) możemy uzyskać dość czytelny wgląd w to, co się dzieje. Powyższa metoda zadziała też na połączeniach sieciowych, w tym na tych, któ©e są już odszyfrowane i idą np. do fcgi po systemowym sockecie.&lt;/p&gt;&#10;&lt;h3 id="na-koniec"&gt;Na koniec&lt;/h3&gt;&#10;&lt;p&gt;Jeśli nie udało się nic znaleźć, a aplikacja dalej się sypie, to pozostaje stara dobra metoda&amp;hellip; wyłącz i włącz kontener, później instancje, a na koniec pogadaj z frontendowcami i backendowcami którzy muszą wysyłać to problematyczne żądanie. Chociaż pogadać i zrozumieć jest dobrze przed całym powyższym szukaniem dziury w całym ;)&lt;/p&gt;</content:encoded></item><item><title>Co jest nie tak z C++</title><link>https://nabozny.io/co-jest-nie-tak-z-c/</link><dc:creator>maciej</dc:creator><pubDate>Mon, 23 Dec 2019 08:22:02 +0000</pubDate><category>Programowanie</category><category>programowanie</category><category>C++</category><guid isPermaLink="true">https://nabozny.io/co-jest-nie-tak-z-c/</guid><description>Czasami mam wrażenie, że podczas rozwoju C++ coś poszło nie tak i język z wersji na wersję stał się nieczytelnym potworkiem, któremu coraz bliżej do Perla z czytelnością. Pomijając wskaźniki i zarządzanie pamięcią, spójrzmy na taki przykład: lista (wskaźników) funkcji, które mają być wywołane po …</description><content:encoded>&lt;p&gt;Czasami mam wrażenie, że podczas rozwoju C++ coś poszło nie tak i język z wersji na wersję stał się nieczytelnym potworkiem, któremu coraz bliżej do Perla z czytelnością. Pomijając wskaźniki i zarządzanie pamięcią, spójrzmy na taki przykład: lista (wskaźników) funkcji, które mają być wywołane po podaniu określonych parametrów programu, z dodatkową listą pomocniczych parametrów i tekstem pomocy. Docelowo ma pozwolić na takie wywołanie aplikacji w następujący sposób:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;./aplikacja --moje-id 1234 --email ja@domena.pl zamow pizze&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wspomniana lista ma trzymać polecenia typu &amp;ldquo;zamów pizze&amp;rdquo;, wymagane dla nich opcje (z --) oraz funkcje/metodę, która to obsłuży, oczywiście ze swoimi parametrami. Plus help text. W C++:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;std::vector&amp;lt;std::tuple&amp;lt;std::vector&amp;lt;std::string&amp;gt; , void(*)(DApp *, std::vector&amp;lt;std::string&amp;gt;) , std::vector&amp;lt;std::string&amp;gt; , std::string &amp;gt; &amp;gt; command_mappings;&#10;...&#10;tuple&amp;lt;vector&amp;lt;string&amp;gt;, void (*)(DApp *, std::vector&amp;lt;string&amp;gt; ), vector&amp;lt;string&amp;gt;, string &amp;gt; p(vector&amp;lt;string&amp;gt;{&amp;#34;zamow&amp;#34;, &amp;#34;pizze&amp;#34;}, handler, vector&amp;lt;string&amp;gt;{&amp;#34;moje-id&amp;#34;, &amp;#34;email&amp;#34;}, &amp;#34;Zamawia pizze na niby&amp;#34;);&#10;command_mappings.push_back(p);&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wiem, można to przerobić na klasę/strukturę i będzie miejscami czytelniej. Wiem też, że można użyć gotowych struktur do obsłużenia tego, ale wyszło jak wyszło.&lt;/p&gt;&#10;&lt;p&gt;W Pythonie:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;command_mappings = []&#10;...&#10;command_mappings.append([&amp;#39;zamow&amp;#39;, &amp;#39;pizze&amp;#39;], handler, [&amp;#34;moje-id&amp;#34;, &amp;#34;email&amp;#34;], &amp;#34;Zamawia pizze na niby&amp;#34;)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Może to trochę jak porówninie Elektrycznego Malucha z Fordem Escortem. Niby lata produkcji te same i oba mają strefę zgniotu kończącą się na silniku, ale jednak technologia i przeznaczenie inne. W pythonie, jak długo programuje, takie kwiatki nie wychodziły w miarę rozwijania kodu, nawet po latach. Kod zawsze wyglądał dość czytelnie i działał wolno. W C++ na odwrót :) A może po prostu &lt;a href="http://google.com/search?q=pebkac"&gt;PEBKAC&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Wesołych Świąt!&lt;/p&gt;</content:encoded></item><item><title>Jak trzymać hasła w pamięci i dlaczego cstringi są czasem lepsze od std::stringów?</title><link>https://nabozny.io/jak-trzymac-hasla-w-pamieci-i-dlaczego-cstringi-sa-czasem-lepsze-od-stdstringow/</link><dc:creator>maciej</dc:creator><pubDate>Sat, 16 Feb 2019 08:32:37 +0000</pubDate><category>Programowanie</category><category>Cloud</category><category>Linux</category><category>kryptografia</category><category>programowanie</category><category>linux</category><guid isPermaLink="true">https://nabozny.io/jak-trzymac-hasla-w-pamieci-i-dlaczego-cstringi-sa-czasem-lepsze-od-stdstringow/</guid><description>“Poznaj sekret cstringów. Std::stringi ich za to nienawidzą”! A na serio, nie zgapiając od pomponików, będzie trochę na temat trzymania wrażliwych danych w pamięci RAM i o zapobieganiu ich zapisowi na dysk.</description><content:encoded>&lt;p&gt;&amp;ldquo;Poznaj sekret cstringów. Std::stringi ich za to nienawidzą&amp;rdquo;! A na serio, nie zgapiając od pomponików, będzie trochę na temat trzymania wrażliwych danych w pamięci RAM i o zapobieganiu ich zapisowi na dysk.&lt;/p&gt;&#10;&lt;p&gt;Na początek krótkie ćwiczenie: napisałeś program do przechowywania haseł. Taki keepass w wersji DIY. Po uruchomieniu wczytuje zaszyfrowany plik do pamięci, pyta Cię o hasło, odszyfrowuje i pokazuje wybrane hasło. Działa dopóki go nie zamkniesz, na wszelki wypadek, jakbyś potrzebował jeszcze innego hasła. Niby bezpieczne, bo zaszyfrowane, na hasło&amp;hellip; ale nie do końca.&lt;/p&gt;&#10;&lt;h3 id="co-może-pójść-nie-tak"&gt;Co może pójść nie tak?&lt;/h3&gt;&#10;&lt;p&gt;Trzymasz odszyfrowany pęk kluczy w pamięci – to pierwszy problem. Dowolne nadpisanie/odczytanie takiej pamięci, np. przez /dev/mem albo /proc/X/mem może skompromitować wszystkie hasła z Twojego pęku – dowolna osoba z uprawnieniami root&amp;rsquo;a będzie mogła wczytać treść z Twojej pamięci. To może odszyfrowywać tylko gdy tego potrzebujemy? Ok, wtedy trzymamy w pamięci jedynie nasze główne hasło i zaszyfrowane przez prawie cały czas dane.&lt;/p&gt;&#10;&lt;p&gt;Osoba atakująca będzie mogła wtedy dość łatwo poznać hasło, którego możesz też przecież używać gdzieś indziej (tak, są tacy ludzie i jest ich dość dużo :) ). Popatrzmy na inny przykład: Linux Kernel posiada wbudowany system szyfrowania dysków – LUKS. Do odszyfrowania jednego dysku można przypisać kilka różnych haseł dla różnych osób. Dzięki temu można uniknąć przekazywania ich i zapisywania na karteczkach. Każdy może użyć wtedy swojego ulubionego hasła :) Jeśli natomiast je zgubi, można zmienić tylko to jedno hasło do dysku, bez zmiany innych haseł. Co więcej zmiana dowolnego hasła nie wymusza re-szyfrowania całego dysku. Jak to działa? Nie ma za tym jakiejś specjalnej magii i matematyki. Każdy z użytkowników, którzy mają swoje hasła tak na prawdę deszyfruje główny klucz do dysku. Dopiero za pomocą tego klucza jest przeprowadzane pełne (de)szyfrowanie.&lt;/p&gt;&#10;&lt;p&gt;Podobny mechanizm możesz też zastosować w Twoim pęku kluczy lub podobnej aplikacji. Za pomocą hasła użytkownik może odszyfrować tak na prawdę klucz symetryczny, który posłuży do odczytania pozostałej części danych. Przechowywanie go w pamięci, jeśli to konieczne, jest wtedy nieco bardziej bezpiecznym rozwiązaniem niż ciągłe przechowywanie hasła, lub co gorsze pełnego pęku kluczy.&lt;/p&gt;&#10;&lt;h3 id="gdzie-zapisać-tymczasowe-hasło"&gt;Gdzie zapisać tymczasowe hasło?&lt;/h3&gt;&#10;&lt;p&gt;Mimo wszystko Twoje hasło lub klucz deszyfrujący musi być tak czy tak chociaż na chwilę wczytane do pamięci. Jak zabezpieczyć się przed jego wyciekiem? Oprócz możliwości odczytania hasła bezpośrednio z pamięci jest jeszcze jedna możliwość. Atakujący może zaalokować tak dużo pamięci w systemie, że w końcu Twój DIY Keepass zostanie przeniesiony do swapu. A stąd już prosta droga do wyciągnięcia i przeanalizowania danych. Jeśli nie jako dump pamięci z poziomu roota, to śrubokrętem, przenosząc dysk do innego komputera lub bootując komputer z live cd.&lt;/p&gt;&#10;&lt;p&gt;Jak się przed tym obronić? Linux posiada trzy funkcje pozwalające Twojej aplikacji określić jak ma się zachowywać jej wirtualna pamięć. Pierwsza z nich to &lt;a href="https://linux.die.net/man/2/mprotect"&gt;&lt;em&gt;mprotect&lt;/em&gt;&lt;/a&gt;. Podając adres pamięci (np. blok zwrócony przez &lt;em&gt;malloc&lt;/em&gt;) możesz sprawić, że dany obszar będzie dostępny tylko do odczytu, do zapisu lub w cale. Flaga &lt;em&gt;PROT_WRITE&lt;/em&gt; jest zwykle stosowana do zabezpieczenia obszaru pamięci, w którym znajduje się Twój plik wykonywalny, aby przypadkowe (lub złośliwe) nadpisanie kodu instrukcji Twojej aplikacji nie było możliwe. &lt;em&gt;PROT_EXEC&lt;/em&gt; jest natomiast stosowane do zabezpieczenia pamięci przed wykonywaniem jej kodu, np. w obszarach pamięci przechwujących zmienne. Zapobiega do przypadkowemu uruchomieniu takiego kodu. Wbrew pozorom bardzo użyteczną flagą może być &lt;em&gt;PROT_NONE&lt;/em&gt;. Takiego obszaru nie da się ani odczytać, ani zmodyfikować ani wykonać. Jedną stronę takiej pamięci możemy ustawić np. za obszarem, do którego będą zapisywane dane od użytkownika, o nieznanym rozmiarze. Jeśli nasze programowe zabezpieczenia zawiodą i na przykład zaczniemy zapisywać do takiej tablicy bez opamiętania (bez if&amp;rsquo;a :) to finalnie program spróbuje zapisać coś na stronie pamięci oznaczonym &lt;em&gt;PROT_NONE&lt;/em&gt;. W takiej sytuacji zadziała już sprzętowe zabezpieczenie i nasza aplikacja otrzyma stosowny, zwykle bolesny sygnał od systemu operacyjnego.&lt;/p&gt;&#10;&lt;p&gt;Drugą funkcją służącą do modyfikowania zachowania pamięci jest &lt;a href="http://man7.org/linux/man-pages/man2/madvise.2.html"&gt;&lt;em&gt;madvise&lt;/em&gt;&lt;/a&gt;. Podobnie jak &lt;em&gt;mprotect&lt;/em&gt; przyjmuje ona adres i rozmiar obszaru pamięci, który chcemy zmodyfikować. Za jej pomocą możemy powiedzieć systemowi (doradzić) jak będziemy korzystać z tego obszaru. Czy na przykład będą to losowe odczyty i cache&amp;rsquo;owanie na wyrost nie ma sensu, czy też podczas forka procesu ten obszar pamięci ma pozostać nieskopiowany. Flag jest sporo, warto przeglądnąć je w przyjaznym manualu.&lt;/p&gt;&#10;&lt;p&gt;Ostatnia funkcja, warta wspomnienia to &lt;em&gt;mlock&lt;/em&gt;. Ponownie, przyjmuje jako pierwsze parametry adres i rozmiar obszaru pamięci. Zapobiega ona przenoszeniu stron pamięci na swap. Oznaczając w ten sposób obszar gdzie trzymamy klucz deszyfrujący oraz sam keychain, możemy zabezpieczyć się przed wspomnianym wcześniej wymuszeniem zrzutu pamięci na swap.&lt;/p&gt;&#10;&lt;p&gt;Korzystając z wszystkich trzech powyższych funkcji możemy wymusić na systemie operacyjnym nieco lepsze potraktowanie naszych danych przesiadujących w pamięci. Za pomocą &lt;em&gt;mprotect&lt;/em&gt; możemy przez większość czasu zapobiegać dostępowi do pamięci, ustawiając jej flagę &lt;em&gt;PROT_NONE&lt;/em&gt;, i &amp;ldquo;odbezpieczając&amp;rdquo; tylko gdy użytkownik potrzebuje hasła. Za pomocą &lt;em&gt;madvise&lt;/em&gt; mona zapobiec skopiowaniu tej pamięci przez fork (i prawdopodobnie zabezpieczyć się też na kilka innych okoliczności), a z &lt;em&gt;mlock&lt;/em&gt; zapobiegniemy wyrzuceniu na swap.&lt;/p&gt;&#10;&lt;h3 id="cstring-vs-stdstring"&gt;Cstring vs. std::string&lt;/h3&gt;&#10;&lt;p&gt;Przy okazji wychodzi czemu czasami zarządzanie pamięcią przez &lt;em&gt;malloc&lt;/em&gt; i standardowe stringi z C są bardziej użyteczne. Korzystając z &lt;em&gt;std::string&lt;/em&gt; nie mamy tak dokładnej kontroli nad tym, gdzie będą trzymane dane ze stringa, jak również nie mamy gwarancji że po dopisaniu kolejnych znaków nie zostanie on w całości przeniesiony w inne miejsce. Trudniejsze staje się też zabezpieczenie strony tuż za stringiem. Nie jest to jednak niemożliwe, jednak wymaga dodatkowej pracy i dogłębnej wiedzy jak jest alokowana w tym przypadku pamięć. Dodatkowo, gdy std::string zwalnia pamięć, musimy też ręcznie posprzątać po nim, aby inny obiekt nie dostał obszaru z &lt;em&gt;PROT_NONE&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;W przypadku c stringów, wszystkie operacje przenoszenia, zabezpieczania i odbezpieczania musimy zrobić tak czy tak ręcznie, jednak z punktu widzenia czytelności kodu i jego obsługi jest to znacznie czytelniejsze.&lt;/p&gt;&#10;&lt;h3 id="swap-też-jest-szyfrowany"&gt;Swap też jest szyfrowany&lt;/h3&gt;&#10;&lt;p&gt;Tak, od jakiegoś czasu dystrybucje wspierają to domyślnie. Strony pamięci zrzucane do swapu są w locie szyfrowane, także odczytanie ich po wyłaczeniu komputera nieco się komplikuje. Przykładowy wpis z Arch wiki:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;swap /dev/sdX# /dev/urandom swap,cipher=aes-xts-plain64,size=256&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Przy każdym starcie system generuje losowy klucz do zaszyfrowania swapu z /dev/urandom. Nawet jeśli coś się na swapie pojawi, to będą to tylko losowe znaki dla osoby, która nie zna klucza. Problem pojawia się jednak przy wsparciu dla disk suspend – uśpienia komputera i zrzucenia stanu pamięci na dysk. W tym przypadku sprawy się nieco komplikują, ale to historia na inny wpis.&lt;/p&gt;&#10;&lt;h3 id="wirtualne-maszyny"&gt;Wirtualne maszyny&lt;/h3&gt;&#10;&lt;p&gt;O ile wszystkie powyższe sposoby mają się całkiem dobrze na &amp;ldquo;gołym&amp;rdquo; PC, to już korzystając z wirtualizacji nie mamy gwarancji, że ktoś nie zrobi zrzutu pamięci oraz dysku w locie i nie będzie próbował analizować tego offline. W takim wypadku jedynym rozwiązaniem minimalizującym ryzyko przechwycenia cennych danych jest tylko chwilowe odszyfrowywanie ich. Nie gwarantuje to jednak pełnego bezpieczeństwa. Na własnych systemach można zapobiegać temu odpowiednimi opcjami dla wirtualnych maszyn, dzięki którym gospodarz nie będzie przenosił ich stron na swap. Służy do tego m.in. opcja &lt;a href="https://libvirt.org/formatdomain.html#elementsMemoryBacking"&gt;&lt;em&gt;locked&lt;/em&gt;&lt;/a&gt; w libvircie. Jednak w publicznych chmurach nie mamy takiej gwarancji.&lt;/p&gt;&#10;&lt;h3 id="bezpieczeństwo-różnych-algorytmów"&gt;Bezpieczeństwo różnych algorytmów&lt;/h3&gt;&#10;&lt;p&gt;Na koniec warto wspomnieć o bezpieczeństwie samych algorytmów szyfrujących. Co, jeśli któryś okaże się podatny na ataki? Często aplikacje oferujące szyfrowanie pozwalają na złożenie kilku algorytmów na raz. O ile Twoje hasło pozostaje prawdopodobnie najsłabszym punktem, to szansa na skompromitowanie dwóch algorytmów szyfrujących na raz znacznie spada. Pomyśl, że lata temu używałeś MD5 do potwierdzania integralności plików. Algorytm się &amp;ldquo;zepsuł&amp;rdquo;. Wykonanie SHA1(MD5(&amp;hellip;)) nieco ratuje sprawę. W ten sposób zabezpieczają się np. adresy portfeli z Ethereum. Skompromitowanie jednego algorytmu (co powodowałoby że łatwo wygenerować inny portfel z tym samym adresem) znacznie maleje.&lt;/p&gt;</content:encoded></item><item><title>No masters, no slaves, blockchain inside – pisanie zdecentralizowanych aplikacji</title><link>https://nabozny.io/no-masters-no-slaves-blockchain-inside-pisanie-zdecentralizowanych-aplikacji/</link><dc:creator>maciej</dc:creator><pubDate>Fri, 14 Dec 2018 13:33:22 +0000</pubDate><category>Programowanie</category><category>Cloud</category><category>Linux</category><guid isPermaLink="true">https://nabozny.io/no-masters-no-slaves-blockchain-inside-pisanie-zdecentralizowanych-aplikacji/</guid><description>Blockchain idealnie wpisuje się w ostatni trend politycznej poprawności w świecie IT. Nie ma nodów master ani slave, wszyscy są równi niezależnie od płci miejsca w sieci. Framework Dinemic jest biblioteką, która pomaga tworzyć zdecentralizowane aplikacje w C++ oparte o technologię, która jest …</description><content:encoded>&lt;p&gt;Blockchain idealnie wpisuje się w ostatni trend politycznej poprawności w świecie IT. Nie ma nodów master ani slave, wszyscy są równi niezależnie od &lt;del&gt;płci&lt;/del&gt; miejsca w sieci. Framework Dinemic jest biblioteką, która pomaga tworzyć zdecentralizowane aplikacje w C++ oparte o technologię, która jest pochodną Blockchain, X509 oraz kilku innych technologi. Służy głównie do tworzenia programów, które mają działać na wielu maszynach równocześnie zapewniając przy tym wysoką wydajność i odporność na awarie.&lt;/p&gt;&#10;&lt;p&gt;Chociaż dinemic jest de facto ORM&amp;rsquo;em dla C++, to spróbujmy na początek inaczej. Wyobraź sobie repozytorium Git&amp;rsquo;a. Takie, w którym to nie developerzy robią commity, a poszczególne obiekty klas, a każdy commit jest informacją na temat zmiany stanu takiego obiektu (i bazy danych pod nim), podpisaną przez niego cyfrowo. Dodatkowo pomyśl, że nie mamy jednego centralnego serwera Git&amp;rsquo;a, ale każdy komputer w sieci jest za razem repozytorium lokalnym i remote&amp;rsquo;m dla innych repozytoriów. Jeszcze tylko dodać, że nieautoryzowane zmiany są odrzucane przez inne repa i mamy obraz framework&amp;rsquo;a dinemic. To tak w skrócie. Po nieco obszerniejszy opis zapraszam dalej.&lt;/p&gt;&#10;&lt;h3 id="jak-to-zwykle-działa"&gt;Jak to zwykle działa&lt;/h3&gt;&#10;&lt;p&gt;Zwykle aplikacje, nawet te obsługujące większe serwisy, mają następującą architekturę: program, na przykład w PHP, jest hostowany przez serwer www. Jego logika odpowiada on za przygotowanie i udostępnienie treści zapisanych w bazie danych. W ostatnim czasie, nawet jeśli jest to aplikacja desktopowa lub mobilna, to i tak jej backend jest obsługiwany przez serwis oparty o zapytania HTTP.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2018/12/dinemic-arch1.jpg" srcset="https://nabozny.io/uploads/2018/12/dinemic-arch1_hu_3b6d009c383398e2.jpg 600w, https://nabozny.io/uploads/2018/12/dinemic-arch1.jpg 1143w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="" width="1143" height="740" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;/p&gt;&#10;&lt;p&gt;Jak przy każdym serwisie, podczas budowania takiej aplikacji powstaje cała masa pytań. Jedno z najczęściej spotykanych to jak autoryzować ich użytkowników? W najprostszej formie można to zrobić na poziomie bazy danych, nie komplikując zbytnio logiki swoich aplikacji. Jednak w miarę rozwoju lub chcąc integrować się z zewnętrznymi serwisami pozostaje przeniesienie całej autoryzacji użytkowników do zewnętrznego lub wydzielonego wewnętrznego (mikro)serwisu.&lt;/p&gt;&#10;&lt;p&gt;Na koniec pozostaje do rozwiązania problem dostępności takiej usługi, jej bezpieczeństwa i całej masy innych problemów, na które prawdopodobnie się napotka. Na przykład granulacji uprawnień: czy użytkownik może lub nie mieć dostępu do serwisu, czy też administrator może zdecydować do jakich jego części może mieć dostęp? Jeśli to drugie, to czy chcemy dawać dostęp do wszystkich funkcjonalności, czy mieć możliwość ich wyboru? I tak dalej&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;Do tego może jeszcze dojść zewnętrzny system autoryzacji, np. przez google lub facebooka, i mamy gotowy projekt na długie miesiące służący jedynie do odpowiedniego &amp;ldquo;wpuszczenia&amp;rdquo; użytkownika do naszego systemu. Odpowiedniego, czyli takiego aby nie mógł za dużo nabroić i żeby nie był całkowicie sparaliżowany ograniczeniami. Do tego jeszcze należy pamiętać o zabezpieczeniu wszystkiego, odpowienio mocnych hashah haseł, sposobie hashowania, dedykowanym API&amp;hellip; ech&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://nabozny.io/uploads/2018/11/1.png" srcset="https://nabozny.io/uploads/2018/11/1_hu_3abbf7ffc10fd14a.png 600w, https://nabozny.io/uploads/2018/11/1_hu_e60fd6767eb992f8.png 1200w, https://nabozny.io/uploads/2018/11/1.png 1301w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" alt="" width="1301" height="796" class="alignnone size-full" loading="lazy" decoding="async"&gt;&lt;br&gt;&#10;Architektura OpenStack Havana :) (tak, stare)&lt;/p&gt;&#10;&lt;h3 id="jak-to-działa-z-dinemic"&gt;Jak to działa z dinemic&lt;/h3&gt;&#10;&lt;p&gt;Wyobraź sobie następujący scenariusz z Git&amp;rsquo;a (chociaż dinemic nie ma z nim nic wspólnego). Każdy developer tworzy swoje zmiany we własnych gałęziach. Czasami te zmiany zachodzą na siebie tworząc konflikty, a czasami po prostu konieczne jest zmergowanie dwóch gałęzi w jedną tak, aby połączyć nowe fragmenty kodu z gałęzią stable. Wszystkie takie zmiany są uporządkowane w logiczny ciąg lub nawet graf określający ich kolejność.&lt;br&gt;&#10;&lt;br&gt;&#10;Co się dzieje gdy dwóch developerów pisze w osobnych kopiach repozytorium? Zwykle nic. I jest to bardzo częste gdy wiele osób pracuje na własnych komputerach. Po połączeniu się z repozytorium ich zmiany są nakładane na siebie, a w przypadku konfliktów każdy z nich rozwiązuje je po swojemu.&lt;/p&gt;&#10;&lt;p&gt;Teraz wyobraź sobie bardzo podobny scenariusz, tylko zamiast programistów mamy obiekty klas w C++, których stan zmienia się co jakiś czas. Każda taka zmiana to nowa aktualizacja wpięta w łańcuch lub graf takich aktualizacji, podobnie jak commit w Git. Każda z tych zmian jest też na bieżąco rozsyłana po sieci klastra, w którym działają aplikacje oparte o framework dinemic. W Gicie programiści są identyfikowani kluczami SSH i mogą zmieniać dowolne gałęzie. W framerowku dinemic każdy obiekt klasy podczas jego tworzenia ma generowany zestaw kluczy kryptograficznych, którymi podpisuje się pod swoimi zmianami. Może to robić tylko sam w sobie lub w obiektach, które dodały jego publiczny klucz do listy autoryzowanych obiektów.&lt;br&gt;&#10;&lt;br&gt;&#10;Oprócz tego wszystkie te zmiany są porządkowane w formie łańcuchów umożliwiających odtworzenie stanu klastra z dowolnego momentu i ewentualne połączenie (merge) po awarii klastra.&lt;/p&gt;&#10;&lt;h3 id="bezpieczeństwo-i-uprawnienia"&gt;Bezpieczeństwo i uprawnienia&lt;/h3&gt;&#10;&lt;p&gt;Każdy obiekt, jaki jest tworzony przez klasy dziedziczące po &lt;em&gt;DModel&lt;/em&gt; automatycznie generuje i zapisuje parę kluczy kryptograficznych na hoście, na którym został utworzony. Za pomocą tych kluczy będą podpisane cyfrowo wszelkie zmiany w tym obiekcie, począwszy od informacji o jego stworzeniu, przez zmiany dowolnych pól aż po żądanie skasowania z wszystkich replik w klastrze. Sekretny, prywatny klucz jest tworzony tylko tam, gdzie został stworzony obiekt. Publiczna część klucza jest rozsyłana po całym klastrze wraz z informacją, że obiekt został utworzony w rozproszonej bazie. Na publicznej części klucza opierany też jest ID obiektu, tak więc nie ma możliwości (inaczej – jest to bardzo trudne) podrobienia obiektu o tym samym ID. Jeśli ktoś chciałby to zrobić musiałby złamać algorytmy, na których opiera się znaczna część komunikacji w Internecie.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;libdinemic/dmodel.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;update_network&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// Create new object&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StoreInterface&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SyncInterface&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;authorized_object_ids&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// Restore existing object&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;db_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StoreInterface&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SyncInterface&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;caller&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// Create as child object&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;important_property_a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;important_property_b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DList&lt;/span&gt; &lt;span class="n"&gt;list_of_something&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="text-center"&gt;&lt;small&gt;W dowolnym miejscu w klastrze, a dokładniej w dowolnej jego aplikacji korzystającej z libdinemic można odtworzyć obiekt, który znajduje się w bazie danych i korzystać z niego&lt;/small&gt;&lt;/p&gt;&#10;&lt;p&gt;Ok, ale co po tym, że obiekt można zmienić tylko na hoście, gdzie został stworzony? Dinemic służy do budowania aplikacji, które będą działały na wielu hostach. Koniecznym zatem jest, aby inne hosty w sieci też mogły mieć możliwość odtworzenia takich samych obiektów oraz ich modyfikacji. Pierwsza kwestia jest dość naturalna – tworząc obiekt klasy w jednej aplikacji, nie pojawi się on automatycznie w żadnym innym miejscu. Dopiero chcąc wykorzystać jego dane lub dokonać zmian w tym obiekcie musimy sami, w kodzie aplikacji stworzyć taki obiekt (czyli de facto zmienną tego typu). Dinemic zatroszczy się o to, aby jego stan był zgodny ze stanem na innych nodach klastra:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt; &lt;span class="nf"&gt;my_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;here_comes_long_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;my_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;important_property_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;abcd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="text-center"&gt;&lt;small&gt;W dowolnym miejscu klastra możemy stworzyć zmienną, która zostanie odwzorowana przez ORM Dinemic w obiekt o podanym ID&lt;/small&gt;&lt;/p&gt;&#10;&lt;p&gt;Co do drugiej kwestii, czyli modyfikacji obiektów (również powyższy przykład), to Dinemic daje pełną elastyczność co do definiowania co kto może i jak to może. Po pierwsze przypomnijmy – wszystkie informacje na temat zmian obiektów są zawsze dokonywane przez &lt;em&gt;kogoś&lt;/em&gt;. Jeśli nie definiujemy tego, to są one anonimowe, czyli bez podpisu. W przeciwnym wypadku będą one podpisane kluczem prywatnym obiektu autoryzowanego do dokonania zmian lub podpisane kluczem prywatnym innego, nieautoryzowanego obiektu. Listę takich obiektów można zdefiniować już w trakcie tworzenia pierwszej instancji obiektu w klastrze, przez parametr &lt;em&gt;authorized_object_ids&lt;/em&gt;. Wszystkie zmiany dokonywane przez wymienione tam obiekty będą traktowane tak samo jakby były dokonywane przez ten sam obiekt.&lt;/p&gt;&#10;&lt;p&gt;Weźmy na przykład aplikację, która ma być rozproszonym kalendarzem. Każda osoba (obiekt klasy &lt;em&gt;Person&lt;/em&gt;) ma listę swoich wydarzeń (obiekty klasy &lt;em&gt;Event&lt;/em&gt;). Tworząc nowe wydarzenie możemy podać ID innych osób, które mają mieć możliwość zmian np. terminu. W ten sposób przypiszemy do naszego kalendarza nasze nowe wydarzenie. Nikt poza naszymi dwoma znajomymi nie będzie mógł zmienić tej daty:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;person_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DField&lt;/span&gt; &lt;span class="n"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Map object related to my calendar to new object in code&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="nf"&gt;me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;my_id...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Map John to another object&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="nf"&gt;john&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;jobhn&amp;#39;s_id...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Create new event in database&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authorized_objects&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;authorized_objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_db_id&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;authorized_objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;john&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_db_id&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Event&lt;/span&gt; &lt;span class="n"&gt;new_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authorized_objects&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;W ten sposób John będzie mógł stworzyć taki sam obiekt wydarzenia i wszelkie jego zmiany w tym obiekcie będą traktowane na równi z naszymi:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Event&lt;/span&gt; &lt;span class="nf"&gt;received_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;event_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;received_event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Tomorrow!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Powyższa zmiana, wykonana już w aplikacji John&amp;rsquo;a jest automatycznie propagowana w całym klastrze i po chwili będzie również widoczna w naszym kalendarzu.&lt;/p&gt;&#10;&lt;h3 id="nieautoryzowane-i-anonimowe-zmiany"&gt;Nieautoryzowane i anonimowe zmiany&lt;/h3&gt;&#10;&lt;p&gt;To nie wszystko. Dinemic pozwala zaaplikować dowolny zestaw listenerów, które będą automatycznie odfiltrowywać niepodpisane lub nieautoryzowane zmiany. Dzięki listenerom możemy również nasłuchiwać zmian, jakie są wykonywane w klastrze przez innych uczestników.&lt;/p&gt;&#10;&lt;h3 id="poufne-dane"&gt;Poufne dane&lt;/h3&gt;&#10;&lt;p&gt;Kolejną rzeczą, którą dostaje się gratis jest możliwość szyfrowania danych. Ponieważ dinemic bazuje na otwartej architekturze i domyślnie pozwala akceptować wszystkie zmiany z sieci, programista tworzący w nim aplikacje ma możliwość wykorzystania istniejących kluczy kryptograficznych i zaszyfrowania poszczególnych pól w klasach. Dzięki temu tylko autoryzowane do odczytu obiekty będą miały możliwość odszyfrowania danych zapisanych w bazie i mapowanych przez framework na obiekty C++. Z punktu widzenia programisty wystarczy jedynie zmodyfikować klasę DField lub DList, ustawiając w jej konstruktorze parametr encrypted=true. Wszystkie pola z tą flagą są automatycznie szyfrowane przed wysłaniem do sieci za pomocą kluczy zapisanych w liście &lt;em&gt;read_authorized_keys&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h3 id="jakie-są-największe-różnice-w-stosunku-do-centraljen-bazy-danych"&gt;Jakie są największe różnice w stosunku do centraljen bazy danych?&lt;/h3&gt;&#10;&lt;p&gt;Przede wszystkim Dinemic został stworzony aby zapewnić możliwie wysoką konsystencję danych i stanu klastra w przypadku awarii. Aplikacje w architekturach, które za pomocą logiki dokonują zmian w centralnej bazie danych nie są odporne na awarie sieci. Po dłuższym odcięciu od bazy realny stan klastra może się mocno różnić z zapisanymi w bazie danymi.&lt;/p&gt;&#10;&lt;p&gt;Dinemic postawił ten problem na pierwszym miejscu, przez co dowolne rozcięcie klastra, przez tzw. split brain nie powoduje rozjechania się stanu klastra i bazy danych. Po split brain&amp;rsquo;ie tworzą się de facto dwa oddzielne klastry Dinemic, które zaczynają żyć własnym życiem. Każdy z nich bazuje na ostatniej wersji bazy sprzed split-brain&amp;rsquo;a i modyfikuje tą bazę według własnego uznania. Po rejoin&amp;rsquo;ie, czyli po ponownym połączeniu dwóch części, wszystkie nody zaczynają się wymieniać brakującymi im aktualizacjami stanu.&lt;/p&gt;&#10;&lt;p&gt;W tym punkcje architektura Dinemic zaczyna się znacznie różnić od zwykłej, z centralją bazą danych lub od Blockchain&amp;rsquo;a. Po rejoin&amp;rsquo;ie może nie być możliwe doprowadzenie do konsystencji w takim klastrze za równo na warstwie bazy danych (co zostało przeniesione na barki programisty piszącego aplikacje korzystającą z tego frameworka) jak i na płaszczyźnie konfiguracji i stanu samego klastra. Zamiast tego otrzymujemy węzły klastra, które w miarę możliwości starają się doprowadzić do jednolitego stanu, jednak bez takiej gwarancji.&lt;/p&gt;&#10;&lt;p&gt;Dlaczego tak się dzieje? Popatrzmy na przykład na powyższą aplikację kalendarza. Co jeśli dwie osoby będące w danej chwili offline wprowadziły w swoich kalendarzach dwie sprzeczne zmiany dotyczące tego samego eventu? John przestawił godzinę na 11 rano, a my ustawiliśmy na 8 rano? Trudno podjąć decyzję, która wersja ma być prawidłowa i ostateczna. Co więcej, odrzucenie jednej, tak jak np. w blockchain&amp;rsquo;ie Bitcoin&amp;rsquo;a może doprowadzić do tego, że wydarzenie przesunięte przez Johna na 11 będzie kolidowało nam, ponieważ utworzyliśmy już o tej godzinie inne. Decyzja co w takim przypadku zrobić pozostaje już tylko nam (i John&amp;rsquo;owi). Z najbardziej oczywistych rozwiązań pozostaje nam odrzucenie obu zmian i zaproponowanie nowego czasu, np. przez wyświetlenie odpowiedniego komunikatu. Nie ma tu jednak sposobu na realne zsynchronizowanie planów dnia tych dwóch osób.&lt;/p&gt;&#10;&lt;p&gt;W przypadku tej samej sytuacji w aplikacji z centralną bazą danych, prawdopodobnie jedna z aktualizacji nie doszłaby do skutku, na przykład przez brak łączności lub zostałaby nadpisana przez drugą zmianę.&lt;/p&gt;&#10;&lt;p&gt;Z tego powodu framework Dinemic jedynie dąży do zsynchronizowania stanu obiektów w bazach poszczególnych węzłów klastra, jednak nie wymusza tego sztucznie i nie gwarantuje konsystencji. W zamian za to dostarcza pełen wgląd w historię zmian jakie odbywały się od początku istnienia takich obiektów i daje możliwość podejmowania dziań w zależności od kolejności zdarzeń.&lt;/p&gt;&#10;&lt;h3 id="gdzie-szukać-dalej"&gt;Gdzie szukać dalej?&lt;/h3&gt;&#10;&lt;p&gt;Dokładna instrukcja instalacji oraz dokumentacja frameworka jest dostępna pod adresem: &lt;a href="https://dinemic.io"&gt;https://dinemic.io&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Szukanie dziury w całym – cProfiler dla Pythona</title><link>https://nabozny.io/szukanie-dziury-w-calym-cprofiler-dla-pythona/</link><dc:creator>maciej</dc:creator><pubDate>Sat, 25 Nov 2017 18:14:58 +0000</pubDate><category>Programowanie</category><category>Debugowanie</category><category>cloud</category><category>corecluster</category><guid isPermaLink="true">https://nabozny.io/szukanie-dziury-w-calym-cprofiler-dla-pythona/</guid><description>Czasami wszystko działa, ale chciałoby się lepiej. W poprzednim poście pokazywałem jak poukładać pamięć w aplikacji w C, żeby skorzystać z dobrodziejstw cache procesora. Tym razem trochę o optymalizacji dla Pythona i szukaniu najwolniejszych punktów w programie.</description><content:encoded>&lt;p&gt;Czasami wszystko działa, ale chciałoby się lepiej. W &lt;a href="https://nabozny.io/jak-zrobic-to-samo-nieco-szybciej-organizacja-danych-w-pamieci/"&gt;poprzednim poście pokazywałem&lt;/a&gt; jak poukładać pamięć w aplikacji w C, żeby skorzystać z dobrodziejstw cache procesora. Tym razem trochę o optymalizacji dla Pythona i szukaniu najwolniejszych punktów w programie.&lt;/p&gt;&#10;&lt;h3 id="skąd-się-wziął-problem"&gt;Skąd się wziął problem?&lt;/h3&gt;&#10;&lt;p&gt;Podczas testów kolejnej wersji CoreCluster cały system zaczął działać zadziwiająco wolno – średnio zapytania do API trwały z dnia na dzień coraz dłużej, aż do kilkunastu sekund(!) dzisiaj. Podobnie zachowywała się kolejka zadań dla agentów, mimo ustawienia interwału pobierania zadań na minimum. Pierwsze podejrzenie padło na ostatni refactoring sieci, który wywrócił połowę driverów w chmurze, z modułu CoreNetwork. Ale po pierwsze to dość spory kawałek do przekopania ręcznie, a po drugie drivery obsługujące start systemu na Management Nodzie i Compute Nodach w CoreCluster nie mają nic wspólnego z API. Jedyna część wspólna do system plików (aczkolwiek nie wszędzie), baza danych i cache (Redis). Jak znaleźć to coś, co łakomie zjada czas i doprowadza użytkownika do irytacji?&lt;/p&gt;&#10;&lt;h3 id="zawołajmy-api-przez-firebug"&gt;Zawołajmy API przez FireBug&lt;/h3&gt;&#10;&lt;p&gt;To było pierwsze co można było zrobić – popatrzyć jak CoreUI (interfejs w JavaScript) wywołuje coś z API. Działać działa, ale wolno. I do tego potwierdziło, że kilkanaście sekund jest normą. Co zatem dalej?&lt;/p&gt;&#10;&lt;h3 id="spróbujmy-wywołać-funkcje-bezpośrednio"&gt;Spróbujmy wywołać funkcje bezpośrednio&lt;/h3&gt;&#10;&lt;p&gt;Każda funkcja dostępna przez API CoreCluster musi być odekorowana Pythonową funkcją. Dekorator robi dość sporo roboty związanej z rozpakowaniem jsona, walidacją i autoryzacją użytkownika/noda/tokenu API. Możemy dobrać się do takiej funkcji przez &lt;em&gt;corecluster.views.api.vm.get_list.&lt;strong&gt;function&lt;/strong&gt;&lt;/em&gt;, czyli bezpośrednie wywołanie tego co zostało &amp;ldquo;zawinięte&amp;rdquo; przez dekorator. Taka funkcja potrzebuje kontekstu, w którym dostarczane są informacje o wywołaniu i o uwierzytelnionym &amp;ldquo;wołaczu&amp;rdquo; (ang. Caller, niewiem jak się to tłumaczy :).&lt;/p&gt;&#10;&lt;p&gt;Możemy zatem odpalić konsolę pytkona z prekonfigurowanym shellem dla CoreCluster:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cc-admin shell&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip; i przygotować takie wywołanie:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;corecluster.utils.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;corecluster.models.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;corecluster.views.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;vm&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;maciek&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remote_ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Robimy mniej więcej to, co dekorator. Różnica jest taka, że powyższy kod wykonuje się w ułamku sekundy i problemu nie ma. Szukajmy więc dalej&lt;/p&gt;&#10;&lt;h3 id="prawdziwe-wywołanie-api"&gt;&amp;ldquo;Prawdziwe&amp;rdquo; wywołanie API&lt;/h3&gt;&#10;&lt;p&gt;Po drodze od przeglądarki do funkcji API są:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Sieć&lt;/li&gt;&#10;&lt;li&gt;Nginx&lt;/li&gt;&#10;&lt;li&gt;uWSGI&lt;/li&gt;&#10;&lt;li&gt;Dekorator&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Po Nginxie i uWSGI nie spodziewałbym się niczego złego, podobnie po lokalnej sieci. Wypdało sprawdzić co się dzieje z dekoratorem, gdy funkcja jest wywoływana po Bożemu, już odekorowana. Jak wywołać takie coś, tak samo jak robi to Django? Potrzebny będzie przede wszystkim obiekt &lt;em&gt;HttpRequest&lt;/em&gt;, który jest przekazywany do każdego wywołania w Django. Jest on równocześnie &amp;ldquo;zjadany&amp;rdquo; przez dekorator i zastępowany przez obiekt &lt;em&gt;Context&lt;/em&gt;, który stworzyliśmy ręcznie powyżej.&lt;/p&gt;&#10;&lt;p&gt;Django dostarcza kilka fajnych rzeczy do testowania web aplikacji. Między innymi jest to klasa &lt;em&gt;RequestFactory&lt;/em&gt;, która pozwala łatwo utworzyć obiekt identyczny z tymm, który utworzyłoby Django podczas normalnej pracy:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RequestFactory&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RequestFactory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/api/vm/get_list&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;#34;token&amp;#34;:&amp;#34;6dbedc0c...&amp;#34;}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Drugi parametr w funkcji &lt;em&gt;rf.post&lt;/em&gt; to token, którym uwierzytelniam się w chmurze. Powinien on być zapisany w dokładnie takiej formie, w jakiej wysyła go przeglądarka do API (czyli JSON). Tak stworzony obiekt &lt;em&gt;request&lt;/em&gt;, który jest klasy &lt;em&gt;HttpResponse&lt;/em&gt; możemy przekazać bezpośrednio do funkcji API, już bez wołania &amp;ldquo;zawiniętej&amp;rdquo; wrapperem wewnętrznej funkcji:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;corecluster.views.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;vm&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Takie wywołanie trwa już dość długo i można można z dość dużym prawdopodobieństwem szukać problemu w samym dekoratorze. Wywołanie samej odekorowanej funkcji trwało krótko, a tutaj wywołujemy tą samą funkcję, tylko za pośrednictwem dekoratora.&lt;/p&gt;&#10;&lt;h3 id="czy-już-zamykać-się-w-piwnicy-i-szukać-przez-całą-noc-błędu"&gt;Czy już zamykać się w piwnicy i szukać przez całą noc błędu?&lt;/h3&gt;&#10;&lt;p&gt;Nie :) Jest jeszcze coś innego, co warto użyć – profiler dla Pythona. Jest to narzędzie, które pokaże ile razy jakie funkcje są wywoływane w naszym (i nie tylko) kodzie. Świetnie się sprawdzi właśnie przy szukaniu takich wolnych punktów w naszym kodzie. Nie służy ono do poprawienia stabilności i przetestowania kodu, ale do poprawienia jego wydajności.&lt;/p&gt;&#10;&lt;p&gt;Spróbujmy użyć go do wykonania powyższego skryptu:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cProfile&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;cProfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;vm.get_list(request)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tottime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Na pierwszy rzut oka wydaje się dość proste. Za pomocą funkcji &lt;em&gt;run&lt;/em&gt; wywołujemy to, co nas interesuje – funkcje vm.get_list z podanym jako parametr obiektem request.&lt;/p&gt;&#10;&lt;p&gt;Po wykonaniu cProfile pokaże całą listę wywołanych funkcji, posorotwaną wedug &lt;a href="https://docs.python.org/2/library/profile.html"&gt;kryterium&lt;/a&gt; jakie podaliśmy:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;cProfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;vm.get_list(mr)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tottime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;45373&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;43564&lt;/span&gt; &lt;span class="n"&gt;primitive&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="mf"&gt;0.105&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Ordered&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;internal&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ncalls&lt;/span&gt; &lt;span class="n"&gt;tottime&lt;/span&gt; &lt;span class="n"&gt;percall&lt;/span&gt; &lt;span class="n"&gt;cumtime&lt;/span&gt; &lt;span class="n"&gt;percall&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;799&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;442&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.008&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="n"&gt;functional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="fm"&gt;__get__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;53&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.016&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mi"&gt;2154&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2142&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.002&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;377&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.004&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;647&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;as_sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;58&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.012&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1117&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;build_filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;37&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.013&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;167&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_select&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;125&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.004&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1277&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names_to_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;37&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.024&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;371&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;as_sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.005&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;492&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_default_columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;238&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="mf"&gt;0.002&lt;/span&gt; &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="n"&gt;related&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;589&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foreign_related_fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dostajemy (tutaj tylko fragment) listę wszystkich funkcji, które były wywołane podczas uruchamiania podanej do cProfilera linijki kodu. Sortując wg. parametru &lt;em&gt;tottime&lt;/em&gt; możemy sprawdzić na czym najwięcej czasu spędził program.&lt;/p&gt;&#10;&lt;h3 id="gdzie-był-problem"&gt;Gdzie był problem?&lt;/h3&gt;&#10;&lt;p&gt;W moim przypadku okazało się, że problem tkwił w nieskonfigurowanej bazie logów dla CoreCluster. Po dłuższym używaniu domyślna baza Sqlite zapchała się logami i spowalniała cały system. Nie pomagało też ich okresowe dumpowanie przez zadanie Cron, ponieważ testowa instalacja nie działała w nocy. Po zmianie na MySQL wszystko powróciło do normy i całość działa dobrze.&lt;/p&gt;&#10;&lt;p&gt;Natomiast sam cProfiler dobrze znać – może się przydać na etapie gdy mamy już gotową aplikację i chcemy wycisnąć z niej jeszcze więcej.&lt;/p&gt;&#10;&lt;h5 id="ps"&gt;ps.&lt;/h5&gt;&#10;&lt;p&gt;Jeśli znasz dobry system dla rozproszonego logowania dla Pythona – inny niż zapisywanie do pliku lub bazy danych – zostaw komentarz :)&lt;/p&gt;</content:encoded></item><item><title>Jak zrobić to samo nieco szybciej – organizacja danych w pamięci</title><link>https://nabozny.io/jak-zrobic-to-samo-nieco-szybciej-organizacja-danych-w-pamieci/</link><dc:creator>maciej</dc:creator><pubDate>Fri, 24 Nov 2017 14:52:01 +0000</pubDate><category>Programowanie</category><category>programowanie</category><category>C++</category><guid isPermaLink="true">https://nabozny.io/jak-zrobic-to-samo-nieco-szybciej-organizacja-danych-w-pamieci/</guid><description>Krótko na temat tego, czy ten sam algorytm może działać szybciej. Tak! Proste testy, które udało mi się zrobić pokazują wzrost prędkości o ok. 30% przy odpowiednim czarowaniu z danymi, bez ingerencji w sam algorytm.</description><content:encoded>&lt;p&gt;Krótko na temat tego, czy ten sam algorytm może działać szybciej. Tak! Proste testy, które udało mi się zrobić pokazują wzrost prędkości o ok. 30% przy odpowiednim czarowaniu z danymi, bez ingerencji w sam algorytm.&lt;/p&gt;&#10;&lt;p&gt;Skoro nie ruszamy O(n) ani algorytmy, to o czym mowa? O cache procesora i cache instrukcji. Weźmy na przykład ukochanego przez niektórych akademików Quick Sorta. Ze swoim O(n log n) działa szybciej niż Buble Sort z O(n*n). Ale czy na pewno? Na uczelni za to ostatnie pytanie można dostać 2.0 i zostać oddelegowanym na ten sam rok, tylko rok później :)&lt;/p&gt;&#10;&lt;h3 id="l1-l2-l3-ram-i-swap"&gt;L1, L2, L3, ram i swap&lt;/h3&gt;&#10;&lt;p&gt;Większość programistów powinna kojarzyć czym jest cache procesora, ram i swap (lub plik wymiany w znienawidzonych przeze mnie okienkach). Większość też kojarzy jak różni się prędkość zapisu do poszczególnych pamięci, albo chociaż wie, że im bardziej w prawo w tej liście, tym wolniej. Natomiast mało który programista zapytany jak skorzystać z cache procesora będzie wiedział jak to się robi. &amp;ldquo;Gdzieś kiedyś na studiach było, ale dawno i pewnie się pozmieniało, no i pewnie java/python/js zoptymalizuje za mnie&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;A gdyby ktoś Ci powiedział, że układając odpowiednio dane w pamięci możesz przyspieszyć swój kod? No może kosztem jego czytelności.&lt;/p&gt;&#10;&lt;h3 id="l1-i-ram-tak-samo-jak-ram-i-swap"&gt;L1 i RAM tak samo jak RAM i Swap&lt;/h3&gt;&#10;&lt;p&gt;Korzystanie ze swapu jest dla większości oczywiste – gdy coś się nie mieści w RAM&amp;rsquo;ie, to wędruje na dysk. Podobnie jest z cache każdego poziomu. Jeśli procesor nie jest w stanie pomieścić przetwarzanych danych w cache, to wymienia jego zawartość na nowe, aktualnie potrzebne dane z RAM&amp;rsquo;u. I tak jak ze swap&amp;rsquo;em, dzieje się to nie pojedynczych bajtach, ale całych blokach danych.&lt;/p&gt;&#10;&lt;p&gt;Gdybyś miał zrobić program, który nie zmieści się w ramie, to prawdopodobnie zaprojektowałbyś dostęp do pamięci tak, aby jak najmniej skakać po pamięci i powodować wymianę stron z dysku. Podobnie możesz zrobić z przetwarzaniem danych i trzymaniem ich w cache. Efekt nie jest tak zauważalny jak przy swapowaniu, które dosłownie zabija wydajność, ale porównując da się zobaczyć sporą różnicę, nawet 30%.&lt;/p&gt;&#10;&lt;h3 id="zróbmy-przykład"&gt;Zróbmy przykład!&lt;/h3&gt;&#10;&lt;p&gt;Zacznijmy od prostego przykładu – zróbmy z dwóch czarno białych zdjęć jedno, będące średnią jasności. Na przykład:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;image1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;image2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Load images&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;image_diff&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;image_diff&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;image1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Na pierwszy rzut oka kodu nie da się zbytnio zoptymalizować. Jeśli tylko dane są ułożone kolumnami (tj. cała pierwsza kolumna, później cała druga itd.), to nie będziemy odczuwać zbyt dużego efektu przeładowywania cache i program zadziała.&lt;/p&gt;&#10;&lt;p&gt;Jeśli natomiast dane w obrazkach są ułożone wierszami, to już tutaj tracimy cały zysk z cache (które jest ładowane przez procesor transparentnie dla programisty). Dane pierwszego i drugiego piksela leżą w obszarach na tyle oddalonych od siebie, że konieczne jest przeładowanie zawartości cache, co spowolni nasz program do prędkości działania RAM&amp;rsquo;u.&lt;/p&gt;&#10;&lt;h3 id="co-jeszcze-zoptymalizować"&gt;Co jeszcze zoptymalizować?&lt;/h3&gt;&#10;&lt;p&gt;Wydaje się, że takie ułożenie to wszystko, na co możemy się porwać, aby było szybciej. Ale nie – jest jeszcze jedna możliwość. Każda iteracja powyższej pętli odwołuje się do trzech różnych obszarów RAM&amp;rsquo;u. Pierwsze dwa to obrazki źródłowe, natomiast trzeci to obrazek z wynikiem.&lt;/p&gt;&#10;&lt;p&gt;Możemy tak ułożyć nasze dane, aby wszystkie trzy obszary przepleść pomiędzy sobą. Dzięki temu kod programu będzie generował żądanie pobrania bloku danych do cache 3 razy częściej, ale tylko w jednym miejscu kodu i tylko jednego (zamiast oryginalnych trzech). W przetwarzaniu potokowym, w które wyposażona jest teraz większość procesorów spowoduje to, że informacja o konieczności pobrania strony pojawia się dość szybko i jest realizowana, gdy pozostała część procesora jeszcze ma co robić.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Load images...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;W sumie ilość tzw. cache miss w L1, gdy potrzebny obszar pamięci nie jest w cache jest taka sama, ale docelowo zawartość cache L2 (lub niższych) pozwala wczytać te dane trochę szybciej i w praktyce ten sam algorytm wykonuje się ze znaczącą różnicą. W testach na Core2Duo, przy przetwarzaniu 20M bajtów było to około 30%.&lt;/p&gt;&#10;&lt;p&gt;Wracając do naszego akademickiego przykładu Buble Sort vs. Quick Sort – w przypadku dużych dysproporcji w prędkościach przetwarzania danych i ilości tych danych, lepsza złożoność może być &amp;ldquo;zjedzona&amp;rdquo; przez ilość cache-miss generowanych przez Quick Sort, względem Buble Sort idącego prosto przez pamięć, bez skoków. Prawdopodobnie nie będzie to częsty przypadek, ale też należy mieć go na uwadze :)&lt;/p&gt;&#10;&lt;p&gt;Pozostają jeszcze kwestie takie jak instruction cache i przewidywanie skoków w procesorze, ale do tego odsyłam do wujka google.&lt;/p&gt;&#10;&lt;h5 id="zanim-zmierzysz-64ns"&gt;Zanim zmierzysz 64ns&lt;/h5&gt;&#10;&lt;p&gt;Przy testach warto też wziąć pod uwagę optymalizacje kompilatorów. Gcc widząc pętle, z której wyników nikt później nie korzysta (pierwsza, wolniejsza wersja kodu) potrafi ją całkowicie wyciąć z binarnego kodu. Z taką wydajnością wtedy ciężko walczyć :)&lt;/p&gt;</content:encoded></item><item><title>Popen GPG vs. Libsodium</title><link>https://nabozny.io/popen-gpg-vs-libsodium/</link><dc:creator>maciej</dc:creator><pubDate>Sat, 29 Jul 2017 21:38:33 +0000</pubDate><category>Programowanie</category><guid isPermaLink="true">https://nabozny.io/popen-gpg-vs-libsodium/</guid><description>Ten wpis ma na celu pokazać różnice w wydajności programu zbudowanego w oparciu o wywoływanie zewnętrznego polecenia a wykonaniem podobnych zadań za pośrednictwem dedykowanej biblioteki. Przedstawione porównanie nie powinno być jednak brane pod uwagę jako wyznacznik wydajności GPG, a jedynie jako …</description><content:encoded>&lt;p&gt;Ten wpis ma na celu pokazać różnice w wydajności programu zbudowanego w oparciu o wywoływanie zewnętrznego polecenia a wykonaniem podobnych zadań za pośrednictwem dedykowanej biblioteki. Przedstawione porównanie nie powinno być jednak brane pod uwagę jako wyznacznik wydajności &lt;em&gt;GPG&lt;/em&gt;, a jedynie jako ciekawostka, gdyż wykorzystane algorytmy i sposób ich obsługi różnią się pomiędzy GPG a &lt;em&gt;libsodium&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Kilka dni temu zakończyłem refaktoryzację &lt;a href="https://dinemic.io"&gt;libdinemic&lt;/a&gt;, w której zastąpiłem wywoływanie programu GPG biblioteką libsodium, o której pisałem w poprzednim artykule. W dużym skrócie, każda zmiana w bazie na jednym z nodów klastra powinna być podpisana cyfrowo za pomocną klucza prywatnego. W pierwszym prototypie libdinemic korzystało właśnie z GPG jako keystore oraz samo narzędzie do generowania podpisów. Cały ekosystem GPG pozwala na wygenerowanie takiego podpisu na podstawie klucza prywatnego oraz jakiegoś pliku do podpisania.&lt;/p&gt;&#10;&lt;h1 id="co-dokładnie-robi-gpg"&gt;Co dokładnie robi GPG?&lt;/h1&gt;&#10;&lt;p&gt;GPG jest narzędziem, które pozwala podpisywać pliki za pomocą wygenerowanych wcześniej kluczy. W tym celu, przed rozpoczęciem podpisywania wspomniana biblioteka musiała stworzyć parę kluczy, co wydajnościowo nie było dużym problemem, i ile nie zabrakło nam liczb losowych.&lt;/p&gt;&#10;&lt;p&gt;Problem z wydajnością zaczynał się dopiero później. Każdorazowo, gdyjeden z nodów klastra libdinemic miał podpisać cyfrowo zmianę w bazie trzeba było ją zapisać na dysku, w pliku tekstowym. Następnie, za pomocą funkcji &lt;em&gt;popen&lt;/em&gt; libdinemic wywoływał polecenie GPG z odpowiednimi parametrami. &lt;em&gt;Popen&lt;/em&gt; pozwala również odczytać lub zapisać dane do standardowego wejścia / wyjścia wywoływanego programu. Z pozoru mała i prosta funkcja kryje pod sobą dość dużą machinerię: program wywołujący &lt;em&gt;popen&lt;/em&gt; musi wykonać &lt;em&gt;fork&lt;/em&gt;, następnie powtórzyć odpowiednie potoki danych (pipes), przekierować je na wejście lub wyjście i finalnie wywołać &lt;em&gt;exec&lt;/em&gt; w procesie potomnym.&lt;/p&gt;&#10;&lt;p&gt;W obecnych wersjach systemu Linux wszystkie te operacje są bardzo dobrze zoptymalizowane. Na przykład ciężki, jak mogłoby się wydawać &lt;em&gt;fork&lt;/em&gt; nie kopiuje całego procesu, a jedynie oznacza jego pamięć jako &lt;em&gt;copy on write&lt;/em&gt;. Dopiero zmiana danych w poszczególnych stronach pamięci przez zapis do nich wymusza na systemie stworzenie kopii danej strony. Podobnie jest też zaprojektowana funkcja &lt;em&gt;exec&lt;/em&gt;. Jeśli dane polecenie, np. GPG było niedawno używane, to kod programu prawdopodobnie jest w cache w pamięci RAM. Takie mechanizmy mogą wydawać się z początku skomplikowane, a wspomniane cache marnowaniem pamięci, jednak przy zautomatyzowaniu wszystkiego przez system Linux zyskuje całkiem sporo na wydajności.&lt;/p&gt;&#10;&lt;p&gt;Co się dzieje dalej? &lt;em&gt;GPG&lt;/em&gt; wczytuje swoją bazę kluczy, parsuje argumenty linii poleceń, odczytuje stdin i tak dalej&amp;hellip; Ot cała jedna linijka popen.&lt;/p&gt;&#10;&lt;p&gt;Na koniec zostaje oczywiście wczytanie pliku do podpisania i obliczenie podpisu.&lt;/p&gt;&#10;&lt;h1 id="a-co-robi-libsodium"&gt;A co robi libsodium?&lt;/h1&gt;&#10;&lt;p&gt;Ponieważ &lt;em&gt;libsodium&lt;/em&gt; jest biblioteką ładowaną z programem, to po uruchomieniu go wszystkie funkcje są dostępne. I to nie przez wywołanie systemowe, tylko przez skok do funkcji z załadowanej biblioteki, co jest znacznie szybsze. Oczywiście też pozostaje obliczenie podpisu, ale wszystkie dane są przekazywane przez wskaźnik do funkcji biblioteki, co też jest o rzędy wielkości szybsze od odczytu danych z dysku. Dodatkowo możemy zrobić prosty cache kluczy w pamięci, co znacznie przyspieszy wczytywanie ich.&lt;/p&gt;&#10;&lt;h1 id="wydajność"&gt;Wydajność&lt;/h1&gt;&#10;&lt;p&gt;Z pozoru prosta sprawa – oba rozwiązania na pierwszy rzut oka powinny działać tak samo. Wyniki jednak mogą zdziwić. Test był przeprowadzony na komputerze z procesorem Intel Core2Duo i dyskiem SSD. Test libdinemic sprawdza czas wygenerowania 1000 aktualizacji bazy, przetworzenia ich i odczytania. Skupmy się na pierwszej części, w której widać największą różnicę:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Libdinemic + GPG: 15.335s&lt;/li&gt;&#10;&lt;li&gt;Libdinemic + libsodium: 0.43s&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Wyniki wypadają oczywiście na korzyść wersji z &lt;em&gt;libsodium&lt;/em&gt;. Ta wersja programu potrafi wykonać ponad 30 razy tyle, co wersja wykorzystująca &lt;em&gt;popen&lt;/em&gt;. Warto o tym pamiętać korzystając często z wywołań systemowych, zwłaszcza w często wywoływanym kodzie. Przy porównaniu obu metod należy też powiedzieć, że GPG wykorzystuje zupełnie inne algorytmy, które są nieco bardziej zasobożerne. Wchodzi w to również cała otoczka związana z obsługą infrastruktury klucza publicznego. GPG wykorzystuje do kryptografii algorytmy oparte o liczby pierwsze, której moc polega na tym, że odwrócenie niektórych działań trwa wieki. Libsodium wykorzystuje algorytmy oparte o krzywe eliptyczne, których moc polega na magii, z odrobiną sarkazmu i &lt;a href="https://pl.wikipedia.org/wiki/Szyfr_na_krzywych_eliptycznych"&gt;szczypiorku&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Libsodium – kryptografia w łatwiejszym wydaniu</title><link>https://nabozny.io/libsodium-kryptografia-w-latwiejszym-wydaniu/</link><dc:creator>maciej</dc:creator><pubDate>Fri, 21 Jul 2017 21:45:40 +0000</pubDate><category>Programowanie</category><category>kryptografia</category><category>programowanie</category><category>C++</category><guid isPermaLink="true">https://nabozny.io/libsodium-kryptografia-w-latwiejszym-wydaniu/</guid><description>Już kilka razy potrzebowałem użyć kryptografii do jakiegoś zadania. Zwykle przychodził z “pomocą” potworek o nazwie openssl, za pomocą którego można było wyczarować cuda. Problemem była jedynie składnia, którą trudno zapamiętać, bo “potworek” jest szyty na miarę profesjonalnych zastosowań.</description><content:encoded>&lt;p&gt;Już kilka razy potrzebowałem użyć kryptografii do jakiegoś zadania. Zwykle przychodził z &amp;ldquo;pomocą&amp;rdquo; potworek o nazwie &lt;em&gt;openssl&lt;/em&gt;, za pomocą którego można było wyczarować cuda. Problemem była jedynie składnia, którą trudno zapamiętać, bo &amp;ldquo;potworek&amp;rdquo; jest szyty na miarę profesjonalnych zastosowań.&lt;/p&gt;&#10;&lt;p&gt;API &amp;ldquo;potworka&amp;rdquo; jest jeszcze bardziej pokręcone i wymaga ogromnej wiedzy, już na sam początek, co dla niewprawionych w bojach z kryptografią może być i tak bardzo dużym wyzwaniem.&lt;/p&gt;&#10;&lt;p&gt;Niedawno, szukając jakiejś alternatywy dla wołania zewnętrznego polecenia openssl albo gpg, dla projektu &lt;a href="https://github.com/cloudover/libdinemic/"&gt;libdinemic&lt;/a&gt; udało mi się znaleźć bibliotekę Sodium, która jest następcą innego, ponoć podobnego narzędzia NaCl. Biblioteka ta pozwala w bardzo przyjemny sposób ogarnąć podstawowe zadania związane z kryptografią:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Podpisy cyfrowe – generujące kawałek danych potwierdzający, że właściciel prywatnego klucza coś napisał&lt;/li&gt;&#10;&lt;li&gt;Szyfrowanie symetryczne – pozwalające zamienić ciąg ważnych dla nas danych w (prawie) bezużyteczne śmieci, możliwe do odszyfrowania jedynie po podaniu hasła&lt;/li&gt;&#10;&lt;li&gt;Szyfrowanie asymetryczne – pozwalające zaszyfrowanie komuś danych posiadając jedynie jego publiczny, znany wszystkim klucz. Dzięki temu tylko ta osoba ta może odszyfrować, korzystając z prywatnej części klucza&lt;/li&gt;&#10;&lt;li&gt;Funkcje skrótu SHA2&lt;/li&gt;&#10;&lt;li&gt;&amp;hellip; i kilka innych fajnych narzędzi związanych z bezpieczeństwem&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="jak-się-za-to-zabrać"&gt;Jak się za to zabrać?&lt;/h3&gt;&#10;&lt;p&gt;Będziesz potrzebował samą bibliotekę, jej pliki nagłówkowe, kompilator i edytor tekstu:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt-get install libsodium-dev gcc gedit&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aby skompilować coś, co zachwile stworzysz, dodaj opcję &lt;em&gt;-lsodium&lt;/em&gt; do kompilatora:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcc -o program -lsodium program.c&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip;lub skorzystaj z gotowego narzędzia typu &lt;em&gt;make&lt;/em&gt;, &lt;em&gt;qmake&lt;/em&gt; albo &lt;em&gt;cmake&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h3 id="klucz-prywatny-i-publiczny--po-co-to"&gt;Klucz prywatny i publiczny – po co to&lt;/h3&gt;&#10;&lt;p&gt;Zanim przejdziemy do kolejnych kroków – kilka zdań o kryptografii asymetrycznej. Jak źle by to nie było omówione na studiach, warto wiedzieć co ona daje i wierzyć, że jeszcze nikt z NSA nie złamał jej zabezpieczeń (albo i nie wierzyć). W dużym uproszczeniu, kryptografia asymetryczna bazuje na parach kluczy (liczb), z których jedna, prywatna, to zwykle duża losowa liczba. W niektórych algorytmach musi to być liczba pierwsza. Druga liczba, publiczna jest obliczana na podstawie tej pierwszej, prywatnej.&lt;/p&gt;&#10;&lt;p&gt;Generowanie drugiej liczny jest wykonywane w taki sposób, aby operacja odwrotna była bardzo żmudna i długotrwała. Dobrym porównaniem jest mieszanie farbek. Jeśli znasz proporcje (Twój klucz prywatny), to możesz szybko wymieszać podstawowe kolory i otrzymać identyfikujący Cię kolor. Każda wiadomość pomaziana Twoim kolorem będzie automatycznie oznaczona jako nadana przez Ciebie, bo tylko Ty potrafisz zrobić taki kolor. Jeśli natomiast ktoś chciałby podrobić Twój kolor, to musiałby próbować różnych zestawień barw, co trwało by znacznie dłużej. Podobnie miało to miejsce z tajną recepturą KFC. Wszyscy próbowali ją podrabiać, ale do czasu jej wycieku oryginalny smak, bazujący na proporcjach kilkunastu przypraw pozostawał tylko w KFC.&lt;/p&gt;&#10;&lt;p&gt;O ile kolory czy proporcje przypraw można by próbować dość łatwo odtworzyć, bo nie ma aż tylu możliwości, to obliczenie prywatnego klucza kryptograficznego (proporcji przypraw) na podstawie klucza publicznego (smaku) może trwać zwykle latami na pojedynczym komputerze. Dla tego jest to uznawane za bezpieczny środek komunikacji, a dodatkowo daje możliwość udostępnienia wszystkim czegoś (smaku, koloru, publicznego klucza), który może zweryfikować czy to my lub umożliwić zaszyfrowanie nam wiadomości, którą odczytamy tylko my.&lt;/p&gt;&#10;&lt;h3 id="podpisy-cyfrowe"&gt;Podpisy cyfrowe&lt;/h3&gt;&#10;&lt;p&gt;Pierwszym zadaniem z &lt;em&gt;libsodium&lt;/em&gt; było dla mnie zakodowanie modułu umożliwiającego generowanie podpisów cyfrowych w &lt;em&gt;libdinemic&lt;/em&gt;. Miało to uwierzytelnić każdą zmianę wykonywaną w rozproszonej bazie dla wielu hostów, bez korzystania z blokad i tym podobnych mechanizmów. W zamian miały być wykorzystane właśnie podpisy cyfrowe – tylko osoba posiadająca klucz prywatny przypisany do obiektu w bazie może go zaktualizować.&lt;/p&gt;&#10;&lt;p&gt;Jak wygenerować taki podpis za pomocą &lt;em&gt;libsodium&lt;/em&gt;? Potrzebujemy wiadomość (update stanu bazy) oraz parę kluczy, prywatny i publiczny. Klucze można wygenerować jedną funkcją:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;sodium.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;sign_secret_key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;crypto_sign_SECRETKEYBYTES&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;sign_public_key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;crypto_sign_PUBLICKEYBYTES&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;crypto_sign_keypair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sign_public_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sign_secret_key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;crypto_sign_keypair&lt;/em&gt; zapisze do podanych zmiennych klucz prywatny (losowy ciąg danych) i odpowiadający mu klucz publiczny. Libsodium używa do tego algorytmów opartych o krzywe eliptyczne, przez co klucze są znacznie krótsze od tych znanych z RSA. Mając dwa klucze możemy wygenerować wiadomość i podpisać ją cyfrowo naszym prywatnym kluczem:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;crypto_sign_BYTES&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Ala ma kota a kot ma Ale&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;crypto_sign_detached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sign_secret_key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Podpis cyfrowy będzie zapisany do zmiennej sig, która ma stałą, określoną przez crypto_sign_BYTES długość.&lt;/p&gt;&#10;&lt;p&gt;W ten sposób możemy rozpowszechnić nasz publiczny klucz, za pomocą którego inne osoby lub programy będą mogły potwierdzić autentyczność naszej wiadomości. Aby zweryfikować podpisany ciąg znaków wystarczy sprawdzić:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;crypto_sign_BYTES&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;//pobieramy podpis&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;//pobieramy wiadomość&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;crypto_sign_verify_detached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sign_public_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// To jest fałszywy podpis!&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Podpis się zgadza&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="szyfrowanie-danych"&gt;Szyfrowanie danych&lt;/h3&gt;&#10;&lt;p&gt;Szyfrowanie danych z libsodium wygląda podobnie, aczkolwiek wymaga użycia innego zestawu funkcji i algorytmów. Po pierwsze musimy wygenerować inny zestaw kluczy za pomocą innej funkcji – &lt;em&gt;crpto_box_keypair&lt;/em&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;encryption_secret_key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;crypto_box_SECRETKEYBYTES&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;encryption_public_key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;crypto_box_PUBLICKEYBYTES&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;crypto_box_keypair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encryption_public_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encryption_secret_key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Następnie możemy rozpowszechnić publiczny klucz. Szyfrowanie działa trochę inaczej – tutaj do wygenerowania danych nadawcy potrzebny jest publiczny klucz. Dzięki temu tylko odbiorca może odszyfrować swoją wiadomość. Odwrotnie było w przypadku podpisów – tylko nadawca mógł się podpisać pod wiadomością, a zweryfikować mógł ktokolwiek, używając jego klucza publicznego.&lt;/p&gt;&#10;&lt;p&gt;Do zaszyfrowania wiadomości będziemy potrzebowali dodatkowego obszaru pamięci, do którego libsodium zapisze zaszyfrowane dane:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Ala ma kota a kot nie ma Ali&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;encrypted_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;crypto_box_SEALBYTES&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;crypto_box_seal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encrypted_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;encryption_public_key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Wysylamy wiadomosc w swiat&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Odbiorca wiadomości może ją odszyfrować posiadając swój prywatny klucz:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Odebranie wiadomosci&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;decrypted_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;encrypted_message_length&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;crypto_box_seal_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decrypted_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;encrypted_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;encrypted_message_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;encryption_public_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;encryption_secret_key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// nie udalo sie odszyfrowac wiadomosci&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Mamy wiadomosc!&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Do powyższych przykładów należy podejść czysto edukacyjnie i zwrócić uwagę zwłaszcza na funkcje &lt;em&gt;strlen&lt;/em&gt;, które w przypadku danych binarnych mogą zwrócić niepoprawne wartości.&lt;/p&gt;</content:encoded></item></channel></rss>