Uncategorized Archives - Miejsce dla ludzi, których serce bije kodem

Sprytne rozwiązania – RabbitMQ

W każdym z systemów, obsługujących duży ruch sieciowy, w pewnym momencie spotykamy się z ograniczeniami komunikacji synchronicznej. W ciągu dnia zdarzają się duże, przejściowe wzrosty ruchu, które możemy rozłożyć na większą liczbę instancji systemu, co może okazać się zbędne, są bowiem sprytniejsze rozwiązania.

Nie wszystkie żądania muszą być przetworzone dokładnie w tym samym momencie. Większość z nich może poczekać na swoją kolej i będzie to jak najbardziej akceptowalne z biznesowego punktu widzenia – dzięki temu zmniejszą się chociażby problemy z wydajnością oraz infrastrukturą obsługiwanego przez nas systemu. Jak to osiągnąć? Warto skorzystać z systemu kolejkowego, a o jego funkcjonowaniu na konkretnym przykładzie opowiemy w tym artykule.

Gdy komunikacja synchroniczna nie wystarcza – kolejki

Kiedy już nadejdzie ten moment, w którym przetwarzanie synchroniczne przestanie być wydajne, trzeba znaleźć rozwiązanie rodzącego się problemu. Jedną z najpopularniejszych opcji jest wprowadzenie komunikacji asynchronicznej  i zastosowanie kolejek. A czym właściwie są kolejki?

http://www.quickmeme.com/meme/3qdzk7

Kolejka to abstrakcyjny typ danych, który przechowuje uporządkowaną, liniową sekwencję elementów. Można to opisać jako strukturę FIFO (first in, first out) – pierwszy element, który zostanie dodany do kolejki, będzie pierwszym, który ją opuści. Taką kolejkę można przyrównać do zwykłej kolejki w sklepie lub na koncercie, gdzie pierwsza osoba, która w niej staje, będzie jako pierwsza obsłużona po czym z niej wychodzi. Proste i… sprawdza się.

Obsługa kolejek, czyli jakie wybrać narzędzia?

Uzbrojeni w kluczową wiedzę o kolejkach, w następnym kroku poszukujemy narzędzia do ich obsługi. Możemy je oczywiście napisać sami, ale o wiele lepiej jest skorzystać z istniejących i sprawdzonych rozwiązań. Dwa najpopularniejsze, dedykowane pod obsługę kolejek to Apache Kafka oraz RabbitMQ. W jednym z projektów zdecydowaliśmy się na wybór RabbitMQ, między innymi ze względu na jeden z driverów architektonicznych – w tym przypadku są to ograniczenia projektowe. Ponadto Deweloperzy z Zespołu znali najlepiej właśnie to rozwiązanie i biorąc pod uwagę optymalizację czasu pracy, miało dla nas najniższy próg wejścia. Czym zatem jest wspomniane RabbitMQ?

RabbitMQ – broker wiadomości

RabbitMQ to broker wiadomości – pośrednik w komunikacji. Opiera się na protokole AMQP. Posiada oficjalne biblioteki klienckie napisane w wielu językach, takich jak: Java, Python czy też PHP. RabbitMQ to lekkie i niezwykle wydajne narzędzie, które wspomaga komunikację w systemach rozproszonych.

Poszczególne podsystemy w naszej aplikacji mogą komunikować się ze sobą za pomocą RabbitMQ z użyciem mechanizmu wiadomości (np. Symfony Messenger),  ale nie tylko. System ten  pozwala na ich publikowanie, późniejszą subskrypcję i przetwarzanie dzięki konsumerom. Ilość konsumerów możemy zwiększać elastycznie w zależności od potrzeby, co ułatwia nam wydajne skalowanie aplikacji. Dzięki temu jest ona w stanie obsłużyć ruch sieciowy, na który nie byliśmy wcześniej przygotowani.

Wiele mocnych stron RabbitMQ, w tym jego elastyczność, pochodzi ze specyfikacji AMQP. W przeciwieństwie do protokołów, takich jak HTTP i SMTP, specyfikacja AMQP definiuje nie tylko protokół sieciowy, ale także usługi oraz zachowania po stronie serwera.

Dodam, że rozszerzenie skrótu AMQP to Advanced Message Queuing Protocol, a pod linkiem https://www.amqp.org/about/what znajdziecie więcej przydatnych informacji na ten temat.

RabbitMQ  ma pięć podstawowych składników, które należy zrozumieć, aby w świadomy sposób posługiwać się tym narzędziem. Niżej opisane zostaną one w skrócie, a jeżeli jesteście zainteresowani szczegółami, możecie znaleźć je w dokumentacji: https://www.rabbitmq.com/tutorials/amqp-concepts.html.  

https://www.gokhan-gokalp.com/en/rabbitmq-nedir-ve-windowsa-kurulumu/hello-world-example-routing/

Zatem podstawowe elementy RabbitMQ  to:

  1. Publisher – tworzy i wysyła wiadomości do exchange. Zazwyczaj będzie to fragment naszej aplikacji.
  2. Exchange – podejmuje decyzję, w których queues umieścić wiadomość.
  3. Routes lub Binding – różne reguły, które łączą exchange z queue.
  4. Queue – przechowuje wiadomości.
  5. Consumer – może być to oddzielna aplikacja lub fragment naszej aplikacji, która pobiera wiadomości z queue i następnie je przetwarza.

Chcąc bardziej zobrazować temat queue, można go przyrównać do sklepu, w którym chcesz zrobić zakupy, exchange będzie magazynem a binding to trasa prowadząca z magazynu do sklepu. Co ważne, zawsze istnieje wiele dróg łączących opisane miejsca.

Wybierając narzędzie do obsługi kolejek trzeba zastanowić się nad biblioteką kliencką, która pozwoli na komunikację z RabbitMQ. W naszym przypadku wybór padł na Symfony Messenger. Pisanie message i message handlerów z użyciem tego narzędzia jest bardzo intuicyjne. Oprócz tego konfiguracja exchange i queue odbywa się w jednym miejscu. Warto podkreślić, że Symfony Messenger jest agnostyczny co oznacza, że system kolejkowania można oprzeć o RabbitMQ, Kafkę, Redis, relacyjną bazę danych, jak i inne narzędzia. Teraz określmy dokładnie, czym jest Symfony Messenger. 

Kilka słów o Symfony Messenger

Messenger jest komponentem frameworku Symfony. Zapewnia możliwość wysyłania wiadomości, a następnie ich natychmiastowej obsługi w aplikacji (przetwarzanie synchroniczne) lub wysyłania ich przez transporty do późniejszej obsługi (przetwarzanie asynchroniczne).  Symfony Messenger pozwala w prosty sposób zaimplementować CQRS pattern (https://martinfowler.com/bliki/CQRS.html) w naszej aplikacji.

Instrukcję instalacji Symfony Messenger i dodatku Symfony Messenger do AMQP można znaleźć  w dokumentacji: https://symfony.com/doc/current/messenger.html. Warto zwrócić uwagę na to, że w sytuacji, w której wyspecyfikujemy, że Symfony Messenger ma stosować RabbitMQ, komponent ten korzysta „pod spodem” z następującej biblioteki https://github.com/pdezwart/php-amqp.

Zabierzmy się teraz za implementację. Postaramy się jasno przedstawić, jakie możliwości konfiguracji zachowań RabbitMQ daje nam Symfony Messenger oraz w jaki sposób wysyłać i odbierać z jego pomocą wiadomości.  

Konfiguracja kolejek, exchange i bindingów w Symfony Messenger

Konfiguracja exchange, kolejek i bindingów w RabbitMQ z użyciem Symfony Messenger jest intuicyjna, wystarczy zmodyfikować plik messenger.yaml. Poniżej można znaleźć przykładową konfigurację wraz z opisem.

Podczas konfiguracji korzystamy z tego, że Symfony Messenger jest agnostyczny. Failure transport (failure_async_priority_high) został skonfigurowany tak, aby czerpał z relacyjnej bazy danych, nie RabbitMQ. Skorzystanie z takiego transportu pod błędnie przetworzone wiadomości zapewnia nam szereg korzyści, m.in:

  • łatwiejsze przeniesienie wiadomości z środowiska produkcyjnego na lokalne,
  • łatwiejsze podpięcie raportów,

Przykładowa implementacja producenta w Symfony Messenger

Tak, jak zostało to opisane wcześniej, producentem może być aplikacja lub fragment naszej aplikacji. W poniższym przykładzie producentem jest Kontroler, który przyjmuje żądania od klientów.

Tworzymy message, nie będzie on zawierał logiki biznesowej, jest on jedynie nośnikiem danych.


namespace App\Messenger;

use JetBrains\PhpStorm\Immutable;

#[Immutable]
class ExampleCommand
{    
    public function __construct(public readonly int $id)
        {
    }
}

Aby skorzystać z Symfony Messenger, „wstrzykujemy” zależność do MessageBusInterface. Wywołujemy na tym obiekcie metodę dispatch.


class ExampleController extends AbstractController
{
    public function __construct(
        private readonly MessageBusInterface $messageBus,
        private readonly SerializerInterface $serializer,
        ){
    }

    #[Route('/index', name: 'app_index', methods: ['POST'])]
    public function index(Request $request): Response
        {
        $message = $this->serializer->deserialize($request->getContent(), ExampleCommand::class, 'json');
        $this->messageBus->dispatch(
            $message,
            [
                new AmqpStamp(null, AMQP_MANDATORY, ['delivery_mode' => 2])
            ]
        );

        return $this->json([
            'message' => 'Index created successfully',
        ]);
    }
}


Pierwszym parametrem wywołania dispatch jest message, który ma zostać obsłużony. Kolejnym parametrem są różnego rodzaju stamp’y . W tym przypadku jest to AmqpStamp, który będzie sterował zachowaniem RabbitMQ w stosunku do wysłanej wiadomości

Przykładowa implementacja konsumera  w Symfony

Konsumer deserializuje command i wykonuje odpowiednią logikę, wykorzystując dane zawarte w command’zie. Logika znajduje się w specjalnych klasach zwanych handler’ami, które implementują interfejs MessageHandlerInterface.


namespace App\Messenger;

use Symfony\Component\Messenger\Handler\Acknowledger;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

class ExampleCommandHandler implements MessageHandlerInterface
{
    public function __invoke(ExampleCommand $message)
        {
        return $message->id;
    }
}


Dzięki autokonfiguracji Symfony , żeby dany message handler mógł być powiązany z określonym message’m, typ argumentu w __invoke musi wskazywać właśnie na klasę message’a. To wystarczy, by po dispatch ExampleCommand był obsłużony przez ExampleCommandHandler. Aby zobaczyć pełną listę powiązanych ze sobą messages  i message handlerów należy wpisać w konsoli:  


php bin/console debug:messenger


Gdy chcesz uruchomić konsumera, wpisz w command line:


php bin/console messenger:consume async_priority_high


Jak wygląda wiadomość wysyłana do RabbitMQ

Podczas publikowania wiadomości, do RabbitMQ klient wysyła tak naprawdę trzy tzw. frame’y. Można je przyrównać do wagonów w pociągu, każdy ma podobną zewnętrzną strukturę, ale przewożą inny ładunek.

Spośród 3 typów wiadomości to dzięki content header, a będąc bardziej konkretnym, dzięki strukturze danych nazwanej Basic.Properties, możemy kontrolować zachowanie RabbitMQ  w stosunku do przesyłanej wiadomości. Content header zawiera metadane o wysyłanej do RabbitMQ  wiadomości, czyli metadane o body. Wykorzystywany jest przez producentów do sterowania zachowaniem RabbitMQ .

Konfigurowanie wysyłania wiadomości do RabbitMQ – Symfony Messenger

Na początek wymieńmy przykładowe basic.properties, które modyfikują zachowanie RabbitMQ w stosunku do wysłanej przed publishera wiadomości. Znajdziecie je w poniższej tabeli.

W dokumentacji Symfony nie jest w jasny sposób przedstawione, jak dodać basic.properties do wiadomości z poziomu Symfony Messenger.  Aby to zrobić, dodajemy do wysyłanej wiadomości odpowiedni AmqpStamp. Implementacja wygląda w następujący sposób:

Podsumowanie 

Z tego tekstu dowiesz się:

  • czym są systemy kolejkowe i kiedy może zajść potrzeba ich wykorzystania,
  • jakie są podstawowe składniki RabbitMQ i Symfony Messenger,
  • jak wysyłać i dobierać wiadomości z użyciem Symfony Messenger,
  • jak tworzyć podstawową konfigurację w pliku messenger.yaml,
  • czym są Basic Properties i jak wysyłać je do RabbitMQ w wiadomościach z użyciem Symfony Messenger.

Połączenie RabbitMQ z Symfony Messenger jest szerokim tematem, a dokumentacja Symfony Messenger często nastręcza problemów. Opisywane narzędzia jednak z powodzeniem podnoszą wydajność aplikacji i rozładowują przejściowe wzrosty ruchu. Żeby móc z nich sprawnie i skutecznie korzystać, warto oprócz samej konfiguracji poznać wszystkie ograniczenia oraz sposoby na ich obchodzenie.

Jest to pierwszy z artykułów, w którym opisujemy funkcjonowanie RabbitMQ, a będzie ich więcej! Zapraszamy do lektury i śledzenia aktualności na naszym blogu.

Źródła:

PyCon PL 2022 – obecni!

PyCon PL to ogólnopolska konferencja integrująca środowisko programistów Pythona, a także środowiska biznesowe, wykorzystujące ten język w projektach komercyjnych. W listopadzie doczekała się czternastej edycji.

Oczywiście ciekawi branżowej wiedzy oraz nowinek technologicznych wzięliśmy udział w tym wydarzeniu! Co więcej, mieliśmy okazję podzielić się także własnymi doświadczeniami -Kazimierz Najmajer, nasz Senior Data Scientist poprowadził prelekcję Variational Autoencoders.

PyCon co roku skupia polskich i międzynarodowych ekspertów, zapraszając na interesujące prelekcje, a także warsztaty. Tym razem konferencja po przerwie wróciła w formie stacjonarnej, goszcząc kilkaset osób.

Codersi na Infoshare 2022

Infoshare to wydarzenie, które już na stałe wpisaliśmy do naszych kalendarzy. W tym roku wzięliśmy w tym evencie udział po raz kolejny, a drugi raz pod szyldem Silky Coders.

Uczestnictwo w tej największej w Europie Środkowo-Wschodniej konferencji technologicznej zawsze jest niesamowitym przeżyciem i świetną okazją do spotkań i rozmów, a także… podzielenia się wiedzą. Tak było i w tym roku – nasza Product Ownerka Anna Brodecka w ramach swojej prelekcji opowiedziała uczestnikom Infoshare m.in. o tym, co łączy algorytmy ML, RFID i branżę mody – czyli o money mappingu. Jeśli jesteście ciekawi tematu, zapraszamy do obejrzenia całego wystąpienia, które znajdziecie tutaj >>>.

Dla osób, które odwiedziły nasze stoisko, przygotowaliśmy oczywiście także coś specjalnego – możliwość wykonania customu naszych gadżetów.

Teraz pozostaje już tylko odliczać do kolejnej edycji – mamy nadzieję, że do zobaczenia!

Nasz ekspert o komunikacji w czasie cyberataku na łamach Security Magazine

Według danych Accenture liczba cyberataków wzrosła od 2014 r. o 67%. IBM wyliczył z kolei, że średni koszt naruszenia danych w 2021 r. wyniósł 4,24 mln dolarów. Mając to na uwadze specjaliści uważają, że pytanie nie brzmi już „czy”, a raczej „kiedy” firma lub organizacja zostanie zaatakowana bądź zmierzy się z poważnym incydentem bezpieczeństwa. Zwracają też uwagę, że w łagodzeniu jego skutków pomaga odpowiednia komunikacja. To m.in. o tym możecie przeczytać w najnowszym wydaniu „Security Magazine” w artykule, którego współautorem jest Mariusz Prociewicz z Silky Coders! -> https://www.politykabezpieczenstwa.pl/pl/a/07-2022-security-magazine

Codersi na PHPers Summit

PHPers Summit to kolejna branżowa konferencja, na której mogliście nas spotkać!

Przygotowaliśmy na to wydarzenie nasze stoisko, a na nim m.in. konkursy na Kahoot, które przyciągnęły prawdziwe tłumy. Liczne grono zainteresowanych zgromadziła także prelekcja, którą poprowadził Karol Gąsienica, nasz senior software developer – opowiedział o sztuczkach i niebezpieczeństwach w Doctrine.

Cieszymy się, że mogliśmy wziąć udział w tym evencie – dziękujemy organizatorom, a także wszystkim, których mieliśmy okazję podczas niego poznać 🙂

Dane w służbie customer experience

Na portalu Computerworld.pl mieliśmy okazję podzielić się doświadczeniami na temat tego, jak Zespół Silky Coders optymalizuje działania LPP wykorzystując data science. Nasi Specjaliści pracują bowiem nad rozwiązaniami technologicznymi, które pozwalają tworzyć usługi wpływające pozytywnie na szeroko rozumianą efektywność (a nawet efektowność!) biznesu – zarówno z perspektywy klienta, jak i organizacji, w której ma miejsce wiele skomplikowanych procesów.

Dlaczego zatem mówimy, że data science jest motorem napędowym Silky Coders? Odpowiedź znajdziecie tutaj >>>!

Jak działa centrum dowodzenia Silky Coders?

Zapraszamy do wysłuchania podcastu nagranego z Just Join IT, w którym Andrzej Kryżewski, Data Science Director w Silky Coders, opowiada o kuluarach pracy, w której na co dzień realizujemy autorskie projekty IT na rzecz tak dużych marek jak: Reserved, Mohito, House, Cropp i Sinsay.

To u nas rodzą się innowacyjne rozwiązania i to do nas ściągamy najlepszych specjalistów z branży, których serce bije kodem. Co dzięki ich pracy powstaje w naszym centrum dowodzenia? Która z marek rozwija się aktualnie najbardziej? W jakim kierunku zmierzamy? I kogo szukamy do Zespołu?

Posłuchajcie! >>>

Akademia eCommerce, czyli jak wygląda staż w Silky Coders?

Jak rozpocząć karierę w jakiejś branży? Na przykład od stażu! A w przypadku branży IT – najlepiej od stażu w… Silky Coders! Jednym z naszych programów stażowych jest Akademia eCommerce, o której nieco więcej w tym wpisie opowiemy Wam my, a także jej uczestnicy.

Akademia eCommerce ma na celu przygotowanie junior PHP developerówi junior frontend developerów do naszych zespołów obszarowych. To staż, ale dość nietypowy, bo uczestnicy pracują nad własnym produktem – rozwijają naszą wewnętrzną aplikację, przechodząc pełen cykl życia oprogramowania. Sami projektują rozwiązania, zbierają wymagania, a później monitorują produkcję. Będą też mogli sprawdzić się w roli scrum mastera. Z każdym z uczestników stażu wypracowujemy jego osobisty plan rozwoju i przypisujemy mentora.

A co po stażu? Akademia eCommerce trwa maksymalnie 2×3 miesiące. Jeśli stażysta osiągnie oczekiwane cele i zrealizuje wyznaczone zadania, może dołączyć do zespołu naszych Codersów. Na jego miejsce rekrutujemy wówczas kolejnego pasjonata IT.

O tym, jak w praktyce wygląda staż w ramach Akademii eCommerce Silky Coders, moglibyśmy Wam opowiadać długo. Stwierdziliśmy jednak, że najlepiej, jeśli swoimi wrażeniami podzielą się nasi stażyści☺

Filip Warpechowski: Zgłosiłem się na staż w Silky Coders, bo chciałem zacząć coś robić zawodowo już tak bardziej na poważnie. Przyznaję, że trochę się bałem wysłać zgłoszenie, bo to pierwsza taka praca – ale spróbowałem, jestem tu i bardzo mi się podoba. Co najbardziej? To, że się szkolę, cały czas dowiaduję się nowych rzeczy i że działamy w bardzo swobodnej atmosferze, wszyscy mówią do siebie na „Ty”, a jeśli o coś zapytam, to zawsze dostanę wsparcie. To wszystko bardzo pozytywnie mnie zaskoczyło – podobnie jak ilość sprzętu,który dostałem do pracy na stażu (miałem problem z zabraniem się do pociągu, bo działam zdalnie – z Olsztyna 😉 ). Staż jeszcze trwa, ale już czuję, że sporo się nauczyłem. Mogę więc śmiało powiedzieć, że jeśli ktoś się kiedyś będzie zastanawiał, czy warto się tu zgłosić na staż, to: TAK, warto. W Silky Coders na stażu jest super, bo są świetni ludzie i zdobywa się doświadczenie, które jest później bardzo potrzebne.

Maciej Gdula: Dlaczego zgłosiłem się na staż? Pewnie jak większość osób – chciałem rozpocząć pracę w IT. A wybrałem Silky Coders, bo postanowiłem sobie postawić wyzwanie i zmierzyć się z czymś większym. Najbardziej na stażu podoba mi się to, że pracujemy na projektach, które będą realnie wykorzystywane. Mamy przy tym wsparcie osób, które mają już doświadczenie. Rozszerzyłem swoją wiedzę w porównaniu z tym, czego się nauczyłem na studiach, widzę, że cały czas się rozwijam. Podoba mi się też otwartość ludzi tutaj – inni pracownicy są bardzo mili, pozytywnie mnie to zaskoczyło, bo nie spodziewałem się, że będą tak podchodzić do nas – osób, które dopiero zaczynają. Na początku było oczywiście trochę strachu i obaw, jak to będzie wyglądać, czy sobie poradzę itp. Wiadomo, początki są trudne, ale z biegiem czasu zaczyna wszystko wychodzić.

Łukasz Kumor: Zdecydowałem zgłosić się na staż, bo chciałem podszkolić swoje umiejętności. Wcześniej robiłem dużo swoich projektów, ale do tej pory nie pracowałem jako programista, w taki usystematyzowany sposób. O Silky Coders słyszałem już wcześniej i spodobało mi się, gdy dowiedziałem się, że na stażu pracuje się tu nad konkretnym projektem. I faktycznie tak jest – mamy swoje cele, zadania, jesteśmy wdrożeni w scrum i w nim pracujemy, możemy dołożyć coś od siebie. Podoba mi się, że jak mamy z czymś problem, to dostajemy od naszych mentorów wędkę, a nie rybę i to, jaka kultura pracy tu panuje –jest swobodnie, wszyscy są ze sobą na „Ty”, do każdego można napisać i dostać wsparcie. Dzięki temu wszystkiemu szybko się wdrożyłem i poczułem się tu dobrze. Najważniejsza rzecz, jakiej się podczas tego stażu nauczyłem, to chyba działanie w grupie nad kodem i organizacja pracy oraz zrozumienie tego, jakie to jest ważne. Gdyby ktoś zastanawiał się nad tym, czy zgłosić się do Silky Coders na staż, to zapytałbym go, czy faktycznie lubi kodować, tak z pasji. Jeśli tak, to zapraszam 🙂

Szymon Zabrocki: Wysłałem zgłoszenie na staż, bo chciałem zacząć pracować, ale nie miałem doświadczenia, więc potraktowałem to jako szansę na jego zdobycie. Wcześniej robiłem tylko samodzielne projekty. Teraz pracujemy w grupie, więc to dla mnie duża różnica, bo trzeba się podzielić zadaniami, a do tego ktoś tę naszą pracę sprawdza. To już nie jest zadanie na ocenę, tylko realny projekt. Fajnie, że mamy nasz zespół, wszyscy uczymy się od początku, mamy pełną kontrolę nad naszym projektem, ale też wsparcie, a do tego korzystamy z profesjonalnych narzędzi.

Opiekunem naszego programu stażowego jest obecnie Piotr Kosiński–Enterpirse Architect.

To dopiero początki Akademii eCommerce, a już do naszych stażystów dołączają kolejni! Jeśli też chcielibyście postawić swoje pierwsze zawodowe kroki w Silky Coders, śledźcie nasze social media i bloga – będziemy informować także o innych programach stażowych i rekrutacji do nich ☺

Spidersweb.pl pisze o tym, jak z pandemicznego chaosu wyłonili się… Silky Coders

Zanim rozpoczęła się pandemia sprzedaż internetowa odpowiadała za 8% obrotu Grupy LPP, a w ciągu zaledwie kilku miesięcy wzrosła do 50%, stając się głównym kanałem sprzedaży. Co to mogło oznaczać? Przede wszystkim konieczność zadbania o jeszcze bardziej dynamiczny rozwój usług e-commerce, by skuteczniej odpowiadać na potrzeby klientów oraz zapewnić sprawne działanie organizacji, pod szyldem której funkcjonuje aż pięć popularnych brandów. A tak naprawdę to był tylko rozbieg!

Więcej o początkach Codersów, nowych porządkach i nowych technologiach, a także innowacyjnych rozwiązaniach, które już mamy na koncie i które planujemy, przeczytacie na Spidersweb.pl >>>