<?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; corecluster</title><atom:link href="https://nabozny.io/tag/corecluster/index.xml" rel="self" type="application/rss+xml"/><link>https://nabozny.io/tag/corecluster/</link><description>Blog o programowaniu i nie tylko</description><lastBuildDate>Fri, 14 Dec 2018 13:35:34 +0000</lastBuildDate><language>pl-PL</language><generator>Hugo 0.167.0</generator><item><title>Wykorzystanie systemd zamiast crona, initd i automatu do kawy</title><link>https://nabozny.io/wykorzystanie-systemd-zamiast-crona-initd-i-automatu-do-kawy/</link><dc:creator>maciej</dc:creator><pubDate>Sun, 14 Jan 2018 09:55:56 +0000</pubDate><category>Linux</category><category>corecluster</category><category>linux</category><guid isPermaLink="true">https://nabozny.io/wykorzystanie-systemd-zamiast-crona-initd-i-automatu-do-kawy/</guid><description>Kilka lat temu w większości Linuksów zagościł na dobre daemon systemd odpowiedzialny za obsługę startu, całego cyklu życia i wyłączenia systemu operacyjnego. Do czego ten kombajn może się przydać?</description><content:encoded>&lt;p&gt;Kilka lat temu w większości Linuksów zagościł na dobre daemon &lt;em&gt;systemd&lt;/em&gt; odpowiedzialny za obsługę startu, całego cyklu życia i wyłączenia systemu operacyjnego. Do czego ten kombajn może się przydać?&lt;/p&gt;&#10;&lt;h3 id="sysvinit"&gt;Sysvinit&lt;/h3&gt;&#10;&lt;p&gt;Systemd zastąpił dość stary mechanizm zarządzania skryptami startowymi sysvinit, który po załadowaniu jądra był inicjowany przez program /sbin/init. Brał on kolejne serwisy z katalogów &lt;em&gt;/etc/rc[runlevel].d/&lt;/em&gt; i uruchamiał z odpowiednim parametrem. Nowsze lub bardziej rozbudowane dystrybucje potrafiły dodatkowo wprowadzić wymagane serwisy lub podsystemy, które były potrzebne do uruchomienia danego serwisu.&lt;/p&gt;&#10;&lt;p&gt;Systemd wprowadził natomiast format opisowych plików, które pełnią podobną funkcję, natomiast nie są skryptami samymi w sobie, tak jak &lt;em&gt;sysvinit&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h3 id="prosy-serwis"&gt;Prosy serwis&lt;/h3&gt;&#10;&lt;p&gt;Popatrzmy na &lt;a href="https://github.com/cloudOver/corecluster/blob/master/debian/corecluster.service"&gt;przykładowy serwis&lt;/a&gt; z CoreCluster, który startuje podsystem agentów:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[Unit]&#10;Description=CoreCluster cloud&#10;After=network.target&#10;Requires=network.target&#10;&#10;[Service]&#10;Type=simple&#10;ExecStartPre=-/usr/sbin/cc-manage configure&#10;ExecStart=/usr/sbin/cc-manage agent start&#10;ExecStop=/usr/sbin/cc-manage agent stop&#10;&#10;[Install]&#10;WantedBy=multi-user.target&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Zawiera on sekcję &lt;em&gt;Unit&lt;/em&gt;, która opisuje serwis i mówi daemonowi systemd, co jest wymagane do uruchomienia go. Na pierwszy rzut oka wpisy &lt;em&gt;Requires&lt;/em&gt; i &lt;em&gt;After&lt;/em&gt; mogą sprawiać wrażenie zduplikowanych, jednak oba mają pewne znaczenie. Ponieważ systemd działa przez cały czas, od startu systemu i monitoruje stan systemu, możemy określić, że do działania naszego serwisu wymagana jest sieć (parametr &lt;em&gt;Requires&lt;/em&gt;). Oba parametry gwarantują, że po nasz serwis &lt;em&gt;corecluster.service&lt;/em&gt; zostanie po niej uruchomiony. &lt;em&gt;After=network.target&lt;/em&gt; określa kolejność i spowoduje, że serwis będzie mógł być uruchomiony dopiero po uruchomieniu sieci. &lt;em&gt;Requires&lt;/em&gt; spowoduje, że serwis będzie musiał zostać wyłączony jak tylko wyłączy się jakikolwiek z wymaganych serwisów, czyli tutaj sieć.&lt;/p&gt;&#10;&lt;p&gt;Idźmy dalej. Sekcja &lt;em&gt;Service&lt;/em&gt; opisuje co i jak uruchomić. Powyższy przykład pokazuje jak włączyć proces, który zatrzyma się dopiero podczas zatrzymania samego serwisu. Czyli polecenie zawarte w &lt;em&gt;ExecStart&lt;/em&gt; zakończy się. To, jak ma się zachować uruchamiany proces określamy parametrem &lt;em&gt;Type=simple&lt;/em&gt; z tej sekcji. Po uruchomieniu danego polecenia serwis będzie widoczny jako uruchomiony tak długo, jak proces będzie działał i ni zwróci żadnego kodu wyjścia.&lt;/p&gt;&#10;&lt;p&gt;Innym rozwiązaniem są procesy, które odrazu przechodzą do działania w tle lub kończą się (np działają trybie daemon). Zobaczmy na &lt;a href="https://github.com/cloudOver/corenode/blob/master/debian/corenode.service"&gt;przykład serwisu zarządzającego nodami CoreCluster&lt;/a&gt;:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[Unit]&#10;Description=CoreCluster cloud&#10;After=network.target&#10;Requires=network.target&#10;&#10;[Service]&#10;Type=oneshot&#10;ExecStartPre=-/usr/sbin/cc-node configure&#10;ExecStart=/usr/sbin/cc-node start&#10;ExecStop=/usr/sbin/cc-node stop&#10;RemainAfterExit=1&#10;&#10;[Install]&#10;WantedBy=multi-user.target&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Zamiast &lt;em&gt;Type=simple&lt;/em&gt; możemy wpisać &lt;em&gt;Type=oneshot&lt;/em&gt;. Oznacza to, ze proces powinien zakończyć się, a systemd nie przyjmie tego jako błędu.&lt;/p&gt;&#10;&lt;p&gt;W każdym przypadku możemy uruchomić dodatkowe polecenia przed i po uruchomieniu właściwego serwisu. Można dzięki temu skonfigurować środowisko lub posprzątać.&lt;/p&gt;&#10;&lt;h3 id="okresowe-uruchamianie-procesów"&gt;Okresowe uruchamianie procesów&lt;/h3&gt;&#10;&lt;p&gt;Od lat robione cronem. Nikt go nie wyrzuca, ale systemd daje większe możliwości :) Po pierwsze systemd wie więcej o stanie systemu (czy mamy sieć, środowisko graficzne itd). Po drugie systemd pozwala łatwiej kontrolować to, co się dzieje, jeśli jesteśmy rootem.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[Unit]&#10;Description=Run foo weekly and on boot&#10;&#10;[Timer]&#10;OnBootSec=15min&#10;OnUnitActiveSec=1w&#10;Unit=serwis_do_uruchomienia.service &#10;&#10;[Install]&#10;WantedBy=timers.target&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Powyższy przykład uruchomi serwis &lt;em&gt;serwis_do_uruchomienia&lt;/em&gt; (parametr Unit) 15 minut po starcie systemu i następnie co tydzień. Zamiast OnUnitActiveSec możemy też określić w sekcji &lt;em&gt;Timer&lt;/em&gt; konkretne daty, podobnie jak w Cronie: &lt;em&gt;OnCalendar=*-*-* 00:00:00&lt;/em&gt;. Taki wpis spowoduje wykonanie timera dokładnie o północy każdego dnia.&lt;/p&gt;&#10;&lt;h3 id="instalacja-serwisu-i-timera"&gt;Instalacja serwisu i timera&lt;/h3&gt;&#10;&lt;p&gt;Jeśli Twój serwis jest dostarczany przez pakiet instalacyjny deb lub rpm, to możesz zawrzeć opis Twojego serwisu w odpowiednim pliku podczas budowania paczki (patrz przykłady z CoreCluster). W przeciwnym wypadku możesz dodać serwisy jako odpowiednie pliki w katalogu &lt;em&gt;/usr/lib/systemd/system/&lt;/em&gt;. Odpowiednio, pliki serwisów będą miały nazwy kończące się na &lt;em&gt;.service&lt;/em&gt;, czyli np. serwis_do_uruchomienia.service, a pliki timerów &lt;em&gt;.timer&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Aby nasz serwis lub timer zadziałał, musimy go włączyć poleceniem &lt;em&gt;systemctl enable serwis_do_uruchomienia.service&lt;/em&gt;. Spowoduje to, że systemd załaduje ten plik. Jeżeli z jakiegoś powodu będziemy chcieli zmienić jego zawartość (np. zmienić czasy wykonywania timera), to należy później poinformować o tym fakcie demona systemd i przeładować zawartość pliku poleceniem &lt;em&gt;systemctl daemon-reload&lt;/em&gt;. Nie musimy się przy tym martwić linkami symbolicznymi oraz kolejnością uruchamiania serwisów przez systemd, jak to miało miejsce w przypadku sysvinit. Po nieco dłuższym zaznajomieniu się z systemd i jego sposobem zarządzania procesami będziesz mógł bardzo dokładnie dopasować uruchamianie procesów w Twoim systemie. Np. uruchamiając je po podłączeniu konkretnego urządzenia: &lt;em&gt;After=dev-sdb.device&lt;/em&gt; (ich listę znajdziesz po wpisaniu &lt;em&gt;systemctl --all --full -t device&lt;/em&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></channel></rss>