<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[formapro - Medium]]></title>
        <description><![CDATA[Product engineering insights from Forma Pro - Medium]]></description>
        <link>https://medium.com/formapro?source=rss----fbd3ad5ede94---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>formapro - Medium</title>
            <link>https://medium.com/formapro?source=rss----fbd3ad5ede94---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 15:20:37 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/formapro" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Setup Telegram Bot local workspace in minutes.]]></title>
            <link>https://medium.com/formapro/setup-telegram-bot-local-workspace-in-minutes-38a62edc8421?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/38a62edc8421</guid>
            <category><![CDATA[php]]></category>
            <category><![CDATA[bots]]></category>
            <category><![CDATA[telegram]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Max Kotliar]]></dc:creator>
            <pubDate>Wed, 30 Jan 2019 09:55:02 GMT</pubDate>
            <atom:updated>2020-04-29T20:06:10.920Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YT3LfAufG4kUQ0HpwHzHLQ.png" /></figure><p>A few weeks ago I decided to try out something new. Telegram Bot caught my attention so I started playing around with it. The first thing I had to do was to set up a local development environment. The process was a bit tricky because a bot server had to be accessible from the Internet and Telegram required only secured HTTPS connections.</p><p>This article is a complete guide. Once you’ve followed all the steps you have a fully working workspace for developing a chatbot.</p><h4>Create bot</h4><p>Open <a href="https://t.me/botfather">@botfather</a> bot and follow instructions on how to create a new bot. As a result, you should get a token. In a moment, We’ll use it to communicate with Telegram API.</p><h4>Setup Ngrok tunnel</h4><p>Telegram needs HTTPS endpoint available on the Internet. Whenever a user does something in a bot it sends a request containing user action info to the endpoint. Ngrok is a good solution because it is easy to set up and it provides a secure endpoint for free. One disadvantage is that when you stop Ngropk tunnel and start again the new URL is generated. So you have to set webhook again. A paid Ngrok version offers static URL.</p><p>Let’s create a docker network:</p><pre>docker network create bot_ngrok</pre><p>and start Ngrok container and leave it working:</p><pre>docker run --rm -p 4040:4040 --net bot_ngrok \<br>    --name telegram_bot_ngrok wernight/ngrok \ <br>    ngrok http telegram_bot:80</pre><p>Find out your public address:</p><pre>curl $(docker port telegram_bot_ngrok 4040)/api/tunnels</pre><p>The command should print a JSON data. Find public_url field there. The content could be like this https://f2183d0.ngrok.io.</p><h4>Set up telegram webhook</h4><p>Now, we have a tunnel. Let’s instruct Telegram to send updates to that Ngrok URL. For that, we need to do a bit of coding, create a directory and install formapro/telegram-bot library:</p><pre>docker run --rm --interactive --tty --volume $PWD:/$PWD -w $PWD --user $(id -u):$(id -g) composer:1.8 composer req formapro/telegram-bot</pre><p>Create a set_webhook.php PHP script:</p><pre><strong>&lt;?php<br></strong>// set_webhook.php</pre><pre><strong>include_once </strong><em>__DIR__</em>.&#39;/vendor/autoload.php&#39;;</pre><pre><strong>use </strong>Formapro\TelegramBot\Bot;<br><strong>use </strong>Formapro\TelegramBot\SetWebhook;<br><strong>use function </strong>GuzzleHttp\Psr7\str;</pre><pre>$bot = <strong>new </strong>Bot(getenv(&#39;TELEGRAM_TOKEN&#39;));<br>$response = $bot-&gt;setWebhook(<br>    <strong>new </strong>SetWebhook($_SERVER[&#39;argv&#39;][1])<br>);</pre><pre><strong>echo </strong>str($response).<em>PHP_EOL</em>;</pre><p>Execute the script:</p><pre>docker run --rm -v $PWD:/var/www/html -w /var/www/html \<br>    -e TELEGRAM_TOKEN=telegram_token \<br>    formapro/nginx-php-fpm:latest \<br>    php set_webhook.php &quot;https://f2183d0.ngrok.io/get_updates&quot;</pre><p>If it goes well you should see:</p><pre>HTTP/1.1 200 OK</pre><pre>...</pre><pre>{&quot;ok&quot;:true,&quot;result&quot;:true,&quot;description&quot;:&quot;Webhook was set&quot;}</pre><blockquote>Pay attention that if you stop ngrok container and start again the URL will change. So, you have to set up webhook again.</blockquote><h4>Create updates handler</h4><p>Create index.php. It’s a heart of your bot — a script that contains the bot logic:</p><pre><strong>&lt;?php<br></strong>// index.php<br><br><strong>use </strong>Formapro\TelegramBot\Bot;<br><strong>use </strong>Formapro\TelegramBot\Update;<br><strong>use </strong>Formapro\TelegramBot\SendMessage;<br><br><strong>include_once </strong><em>__DIR__</em>.&#39;/vendor/autoload.php&#39;;<br><br><strong>if </strong>(&#39;/get_updates&#39; !== $_SERVER[&#39;REQUEST_URI&#39;]) {<br>    <strong>echo </strong>&#39;Telegram Bot Demo&#39;.<em>PHP_EOL</em>;<br><br>    <strong>exit</strong>;<br>}<br><br><strong>try </strong>{<br>    $requestBody = file_get_contents(&#39;php://input&#39;);<br>    $data = json_decode($requestBody, <strong>true</strong>);<br><br>    $update = Update::<em>create</em>($data);<br><br>    $bot = <strong>new </strong>Bot(getenv(&#39;TELEGRAM_TOKEN&#39;));<br>    $bot-&gt;sendMessage(<strong>new </strong>SendMessage(<br>        $update-&gt;getMessage()-&gt;getChat()-&gt;getId(),<br>        &#39;Hi there! What can I do?&#39;<br>    ));<br><br>    http_response_code(200);<br>    <strong>echo </strong>&#39;OK&#39;.<em>PHP_EOL</em>;<br>} <strong>catch </strong>(\Throwable $e) {<br>    file_put_contents(&#39;php://stderr&#39;, (string) $e);<br><br>    http_response_code(500);<br>    <strong>echo </strong>&#39;Internal Server Error&#39;.<em>PHP_EOL</em>;<br>}</pre><p>Start the bot server and leave it working:</p><pre>docker run --rm -p 80:80 -v `pwd`:/var/www/html --net bot_ngrok \<br>    -e TELEGRAM_TOKEN=telegram_token \<br>    --name telegram_bot \<br>    formapro/nginx-php-fpm:latest</pre><h4>Test the bot</h4><p>Open your bot in Telegram, type /start and in return, you should get Hi there! What can I do?.</p><p>This is it. Now you have a working playground and can continue observing other features of chatbot universe.</p><p>You can also visit <a href="https://forma-pro.com/blog/"><strong>our corporate blog</strong></a> to learn more about up-to-date IT trends and download some useful insights.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=38a62edc8421" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/setup-telegram-bot-local-workspace-in-minutes-38a62edc8421">Setup Telegram Bot local workspace in minutes.</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An elegant way to dump MongoDB using Docker]]></title>
            <link>https://medium.com/formapro/an-elegant-way-to-dump-mongodb-using-docker-c19de290d3fd?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/c19de290d3fd</guid>
            <category><![CDATA[bash]]></category>
            <category><![CDATA[mongodb]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Nikita Serdakov]]></dc:creator>
            <pubDate>Tue, 15 Jan 2019 10:28:57 GMT</pubDate>
            <atom:updated>2020-04-29T19:57:30.213Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*M7S6BDABaEzSVIZv1O4seQ.jpeg" /></figure><p>I had a project that ran on Docker Swarm cluster with MongoDB and in a while, I had to dump and restore DB. I tried to solve it by:</p><ol><li>Adding HTTP endpoint to the application that returned all the collections data. The same for restoring. <em>(cons: polluted app code, complexity, in-house solution).</em></li><li>Hassle with shell scripts that connect to the prod server, make a dump and download it. <em>(good enough for simple Docker but not for Docker Swarm, cuz it makes it hard to get into a container and fetch dump file from it).</em></li></ol><p>I wanted to use official Mongo commands: mongodump and mongorestore but didn’t want to install mongo locally. I ran everything in docker locally so I tried to combine them together:</p><pre>docker run --rm mongo mongodump --host $1 --archive --gzip | cat &gt; dump_$(date &#39;+%d-%m-%Y_%H-%M-%S&#39;).gz</pre><p>So easy and elegant way, where you have only one command and a single-file dump.</p><h4>And what about restore?</h4><pre>cat dump.gz | docker run --rm -i mongo mongorestore --host YOUR_HOST --archive --gzip --drop --db=YOUR_DATABASE_NAME</pre><p>Here I use a little hack of mounting one file into the container: push it into STDIN and read it in the container; -i is necessary to inject it STDIN</p><p>Also added some stuff and it turned into <a href="https://github.com/NikitaKharkov/mongodb-dumper">this</a>.</p><p>Hope will be useful for somebody. Good luck!</p><p><strong>P.S: </strong>this approach could be applied to other databases too.</p><p>You can also visit <a href="https://forma-pro.com/blog/"><strong>our corporate blog</strong></a> to learn more about up-to-date IT trends and download some useful insights.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c19de290d3fd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/an-elegant-way-to-dump-mongodb-using-docker-c19de290d3fd">An elegant way to dump MongoDB using Docker</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deploy and Run Secure Docker Registry]]></title>
            <link>https://medium.com/formapro/deploy-and-run-secure-docker-registry-b9f531280614?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/b9f531280614</guid>
            <category><![CDATA[digitalocean]]></category>
            <category><![CDATA[swarm]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Max Kotliar]]></dc:creator>
            <pubDate>Mon, 12 Nov 2018 08:18:07 GMT</pubDate>
            <atom:updated>2018-11-12T08:18:07.166Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AAMNR2vfrvybN0gwaxBjFQ.jpeg" /></figure><p>This tutorial will guide you through the basic steps of deploying and running a secure Docker registry on Digitalocean.</p><p>Inherently, the registry uses insecure HTTP connections. Because of that, any docker daemon refuses to fetch images from such a registry <a href="https://docs.docker.com/registry/insecure/">unless you force it</a>. Of course, a registry supports secure TLS connection but you have to cook certificates manually and don&#39;t forget to update them once they expire. This is far from to be desired, so let’s automate that with <a href="https://traefik.io/">Traefik</a> and <a href="https://letsencrypt.org/">Let’s Encrypt</a>.</p><ol><li>Create a droplet on <a href="https://m.do.co/c/3674f63946f3">DigitalOcean (referral)</a>. The easiest way is to use “One click apps” called “Docker 18.06.1~ce~3 on 18.04”. Make sure you selected a droplet with enough space. CPU and Memory are less important. Also, make sure you added your SSH key, we will need it in a moment. I assume you named a droplet docker-registry .</li><li>Don’t forget to set up <a href="https://www.digitalocean.com/products/cloud-firewalls/">a firewall</a> to restrict access.</li><li>Configure DSN server to map requests to your domain to a newly created droplet IP address. I assume your registry domain name is registry.foobar.com.</li><li>Init Swarm cluster. Once the droplet is up and running, open terminal and ssh to it. Then run swarm init command:</li></ol><pre>$ ssh root@registry.foobar.com # or ssh root@your_droplet_ip<br>$ docker swarm init --advertise-addr=your_droplet_ip</pre><p>5. Create a docker-compose file on a local computer:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/565bd2593ba45f422151a950ce38c7d1/href">https://medium.com/media/565bd2593ba45f422151a950ce38c7d1/href</a></iframe><p>6. Now, createtraefik.toml file in the same directory:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6c44f8a7ab9077ce537654cf989717dd/href">https://medium.com/media/6c44f8a7ab9077ce537654cf989717dd/href</a></iframe><p>7. Create a deploy.sh script:</p><pre>#!/usr/bin/env bash </pre><pre>scp &quot;docker-compose.yml&quot; &quot;root@registry.foobar.com:/docker-compose.yml&quot;</pre><pre>scp &quot;traefik.toml&quot; &quot;root@registry.foobar.com:/traefik.toml&quot;</pre><pre>ssh &quot;root@registry.foobar.com&quot; &quot;docker stack deploy --compose-file /docker-compose.yml foobar_registry&quot;</pre><p>Make the script executable:</p><pre>chmod u+x deploy.sh</pre><p>8. Deploy registry by running ./deploy.sh</p><p>9. Test it. Open link <a href="https://registry.foobar.com/v2/_catalog."><em>https://registry.foobar.com/v2/_catalog</em></a><em> </em>in a browser.</p><p>Now you could build and push your images to the registry:</p><pre>docker build --tag &quot;registry.foobar.com/foobar/app:latest&quot; .<br>docker push &quot;registry.foobar.com/foobar/app:latest&quot;</pre><p>The code could be found <a href="https://github.com/formapro/private-docker-registry">here</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b9f531280614" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/deploy-and-run-secure-docker-registry-b9f531280614">Deploy and Run Secure Docker Registry</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Symfony Async Commands]]></title>
            <link>https://medium.com/formapro/symfony-async-commands-f05052e3b205?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/f05052e3b205</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[enqueue]]></category>
            <category><![CDATA[symfony]]></category>
            <category><![CDATA[php]]></category>
            <dc:creator><![CDATA[Max Kotliar]]></dc:creator>
            <pubDate>Tue, 14 Aug 2018 08:49:28 GMT</pubDate>
            <atom:updated>2018-08-14T08:49:28.039Z</atom:updated>
            <content:encoded><![CDATA[<p>The upcoming Enqueue release will contain <a href="https://github.com/php-enqueue/enqueue-dev/pull/403">a feature</a> that allows to run Symfony commands asynchronously. You can use it right now, just make sure the package is in place and you have <a href="https://blog.forma-pro.com/getting-started-with-rabbitmq-in-symfony-cb06e0b674f1">EnqueueBundle properly configured</a> plus one of <a href="https://github.com/php-enqueue/enqueue-dev/blob/master/docs/index.md#transports">supported MQ transports installed</a>. The package could be used stand alone or as a part of Symfony application. It runs command with ability to get command output.</p><p>First, install the <a href="https://github.com/php-enqueue/async-command">enqueue/async-command</a> package:</p><pre>$ composer req enqueue/async-command:0.9.x-dev</pre><p>Create the file enqueue_async_commands.yaml and enable the feature there:</p><pre># app/config/packages/enqueue_async_commands.yaml</pre><pre>enqueue:<br>  async_commands: true</pre><p>A simple use case example, that shows how to send a command to message queue so it could be processed later in a consumer.</p><pre>&lt;?php</pre><pre>use Enqueue\Client\ProducerInterface;<br>use Enqueue\AsyncCommand\Commands;<br>use Enqueue\AsyncCommand\RunCommand;<br>use Symfony\Component\DependencyInjection\ContainerInterface;</pre><pre>/** <a href="http://twitter.com/var">@var</a> $container ContainerInterface */</pre><pre>/** <a href="http://twitter.com/var">@var</a> ProducerInterface $producer */<br>$producer = $container-&gt;get(ProducerInterface::class);</pre><pre>$producer-&gt;sendCommand(<br>    Commands::RUN_COMMAND,<br>    new RunCommand(&#39;debug:container&#39;)<br>);</pre><p>Optionally, you can get a command output, as well as exit code and error stream:</p><pre>&lt;?php</pre><pre>use Enqueue\Client\ProducerInterface;<br>use Enqueue\AsyncCommand\CommandResult;<br>use Enqueue\AsyncCommand\Commands;<br>use Enqueue\AsyncCommand\RunCommand;<br>use Symfony\Component\DependencyInjection\ContainerInterface;</pre><pre>/** <a href="http://twitter.com/var">@var</a> $container ContainerInterface */</pre><pre>/** <a href="http://twitter.com/var">@var</a> ProducerInterface $producer */<br>$producer = $container-&gt;get(ProducerInterface::class);</pre><pre>$promise = $producer-&gt;sendCommand(Commands::RUN_COMMAND, new RunCommand(‘debug:container’), true);</pre><pre>// do other stuff.</pre><pre>if ($replyMessage = $promise-&gt;receive(5000)) { <br>   $result = CommandResult::jsonUnserialize(<br>      $replyMessage-&gt;getBody()<br>   );<br>}</pre><pre>echo $result-&gt;getExitCode().PHP_EOL;<br>echo $result-&gt;getOutput().PHP_EOL;</pre><p>Don`t forget that you need a consumer working in a background:</p><pre>$ ./bin/console enqueue:consume --setup-broker -vvv</pre><p>Neat and simple solution. Enjoy!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f05052e3b205" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/symfony-async-commands-f05052e3b205">Symfony Async Commands</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Enqueue. Multi-queue consumption improvements.]]></title>
            <link>https://medium.com/formapro/enqueue-multi-queue-consumption-improvements-c619011a2ce8?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/c619011a2ce8</guid>
            <category><![CDATA[php]]></category>
            <category><![CDATA[enqueue]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[symfony]]></category>
            <dc:creator><![CDATA[Max Kotliar]]></dc:creator>
            <pubDate>Mon, 06 Aug 2018 20:17:11 GMT</pubDate>
            <atom:updated>2018-08-06T20:18:59.542Z</atom:updated>
            <content:encoded><![CDATA[<p>Up till now, <a href="https://github.com/php-enqueue/enqueue-dev">Enqueue</a> consumption performance from multiple queues within one process has been poor. QueueConsumer used round robin algorithm to fetch messages from queues in combination with the blocking receive method.</p><p>The worst case scenario is when you have lots of empty queues and a few with messages. For example, It consumes from five queues and only one has messages and the receive timeout is one second. In this case, it would consume messages one per four seconds. The performance degrades a hundred times. It affected all transports except AMQP.</p><p>Since Enqueue 0.8.35 supports <a href="https://github.com/queue-interop/queue-interop/pull/12">SubscriptionConsumer</a>. The feature has to be enabled explicitly on 0.8.x version and will be the default approach since 0.9. It improves dramatically performance of the worst case scenario and opens a room for further improvements, for instance, Doctrine DBAL transport can fetch messages from multiple queues at one go.</p><p>To try it out, make sure you have at least enqueue\enqueue of 0.8.35 version.</p><p>QueueConsumer:</p><pre><strong>&lt;?php<br><br>use </strong>Enqueue\Consumption\QueueConsumer;<br><br><em>/** </em><strong><em>@var </em></strong><em>\Interop\Queue\PsrContext $context */<br><br></em>$queueConsumer = <strong>new </strong>QueueConsumer($context);<br>$queueConsumer-&gt;enableSubscriptionConsumer(<strong>true</strong>);</pre><p>SimpleClient:</p><pre><strong>&lt;?php<br><br>use </strong>Enqueue\SimpleClient\SimpleClient;<br><br>$client = <strong>new </strong>SimpleClient(&#39;null:&#39;);<br>$client-&gt;getQueueConsumer()-&gt;enableSubscriptionConsumer(<strong>true</strong>);</pre><p>Symfony:</p><pre># app/config/services.yaml<br># composer upd enqueue/enqueue-bundle:^0.8.35</pre><pre><strong>enqueue.queue_consumer.enable_subscription_consumer: </strong>true</pre><p>Enjoy!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c619011a2ce8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/enqueue-multi-queue-consumption-improvements-c619011a2ce8">Enqueue. Multi-queue consumption improvements.</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An efficient way to treat MongoDB in Symfony.]]></title>
            <link>https://medium.com/formapro/an-efficient-way-to-treat-mongodb-in-symfony-97a442d535f6?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/97a442d535f6</guid>
            <category><![CDATA[mongodb]]></category>
            <category><![CDATA[symfony]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Max Kotliar]]></dc:creator>
            <pubDate>Wed, 11 Apr 2018 12:25:22 GMT</pubDate>
            <atom:updated>2018-04-22T06:44:49.254Z</atom:updated>
            <content:encoded><![CDATA[<p><a href="https://www.mongodb.com/">MongoDB</a> is a document database with the scalability and flexibility that you want with the querying and indexing that you need. In addition, it is exceptionally developer friendly. No hassle with table creation, column definitions, data types. The document model maps to the objects in your application code, making data easy to work with.</p><p>In <a href="http://symfony.com/">Symfony</a> world a mainstream solution to work with it is <a href="https://github.com/doctrine/mongodb-odm">Doctrine MongoODM</a> library. Unfortunately, it is <a href="https://github.com/makasim/yadm#benchmarks">extremely slow</a> and forces you to maintain model schema which is effectively kills all profit at the grassroots level. This happens mostly because Doctrine has to traverse an object tree to map values to corresponding object properties or in other words hydrate an object. It also has to do an inverse operation to take a storable data.</p><p>I was not comfortable with that state of things. So I tried to find a better approach that amplify power of the store. What if we use a data array provided by MongoDB as is, just wrap it a little bit by an object. The hydration and persistence would take no time and be as fast as possible. Effectively it is just about setting or getting an array from an object’s property called values. We always have storable data within reach, no need to traverse anything. On the other hand, a cover object plays a role of a contract adding objectish aspects like data encapsulation, methods\properties visibility, type checks, method definitions, interfaces and so on.</p><p>Here’s just a few examples that demonstrates the idea :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8091ccdfd18504aedae733fd5694655f/href">https://medium.com/media/8091ccdfd18504aedae733fd5694655f/href</a></iframe><p>So I started developing a solution around the concept, which is later became known as a schema less, fast hydration and persistence document mapper library called <a href="https://github.com/makasim/yadm">makasim/yadm</a> and the bundle <a href="https://github.com/makasim/yadm-bundle">makasim/yadm-bundle</a>.</p><p>Let’s move to a practical example to see how it all works. For that we have to create a basic project and require the bundle:</p><pre>$ composer create-project symfony/skeleton acme; cd acme;<br>$ composer require makasim/yadm-bundle</pre><p>The model design is a essential starting point so take a look at the imaginary Order model below. It takes a currency, amount, and a number.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6091753f3fcf0887e3993c4061b6c339/href">https://medium.com/media/6091753f3fcf0887e3993c4061b6c339/href</a></iframe><p>The next step is to configure the bundle:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c8f4f5f9ef30f334feee3692e964ff57/href">https://medium.com/media/c8f4f5f9ef30f334feee3692e964ff57/href</a></iframe><p>CRUD examples:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f15fee6365680eb5bde28b4beaaae6eb/href">https://medium.com/media/f15fee6365680eb5bde28b4beaaae6eb/href</a></iframe><p>I’ve showed just a small portion of awesomeness that comes with the bundle.</p><p>There are a lot more so try it in your next Symfony application.</p><p>Have fun and give me feedback on occasion!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=97a442d535f6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/an-efficient-way-to-treat-mongodb-in-symfony-97a442d535f6">An efficient way to treat MongoDB in Symfony.</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RabbitMQ retries on Node.JS]]></title>
            <link>https://medium.com/formapro/rabbitmq-retries-on-node-js-1f82c6afc1a1?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/1f82c6afc1a1</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[rabbitmq]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Anatoliy Bondar]]></dc:creator>
            <pubDate>Thu, 15 Mar 2018 15:10:27 GMT</pubDate>
            <atom:updated>2020-04-29T20:02:12.592Z</atom:updated>
            <content:encoded><![CDATA[<p>In this article I am going to show you how to implement a robust retry topology using node.js and <a href="https://www.npmjs.com/package/amqplib"><strong>amqplib</strong></a> npm library. The solution is inspired by <a href="https://github.com/php-enqueue/enqueue-dev/blob/master/pkg/amqp-tools/RabbitMqDlxDelayStrategy.php">RabbitMqDlxDelayStrategy.php</a> strategy from Enqueue library.</p><p>First of all, here is our topology:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/636/1*hSW5xNgYPTFCgllyt4_95A.png" /></figure><p>Lets have a brief overview on how retrying works.</p><p>1) Imagine something goes wrong in a <strong>comments</strong> queue consumer. Say, there is an exception or reject.</p><p>2) We acknowledge an original message, create its clone and send it to TTL EXCHANGE with routing key = <strong>retry-1</strong></p><p>3) Message goes to corresponding TTL queue —<strong> comments-retry-1-30s</strong> (ttl queue1 30s on image above) with TTL = 30s</p><p>4) This queue does nothing, even doesn’t have any consumers connected. The message is expired there and therefore moved to corresponding DLX (Dead Letter Exchange).</p><p>5) DLX is configured to send all messages to our payload queue <strong>comments</strong>, so we have a second try now.</p><p>6) If it fails again, do repeat, but this time publish the cloned message to <strong>comments-retry-2-10m </strong>queue.</p><p><strong>The consumer might lost the connection with RabbitMQ (consumer crashed/killed or connection issues). In this case we should do next:</strong></p><ol><li>RabbitMQ notices that connection has been lost and therefore return the message to the queue, marking it as <strong>redelivered.</strong></li></ol><blockquote>You can read more about redelivery pit falls in the <a href="https://blog.forma-pro.com/rabbitmq-redelivery-pitfalls-440e0347f4e0">article</a></blockquote><p>2) When the consumer recovers it handles that message again. Now it checks the <strong>redelivered </strong>flag. If it’s true it DOSN’T EVEN TRY TO HANDLE IT - just sends it to retry flow. After it goes through the whole retry flow, the message will come back without <strong>redelivered</strong> mark and will be handled as always.</p><p>Why don’t we handle <strong>redelivered</strong> messages and just send them to retry? Because we don’t know for sure what caused that redelivery. What if this message is broken (say we try to handle an old message with a new code) and each time it is handled a consumer crashes? So instead of having endless loop of crashes we have maximum 3 crashes and then the message is erased.</p><p><em>Each work (payload) queue (like </em><strong><em>comments</em></strong><em> and so) has its own set of retry queues and exchanges: 1 TTLX, 3 ttl queues and DLX</em></p><p><strong>Implementation</strong></p><p>First of all let’s create our <strong>comments</strong> queue and set up retry queues and exchanges:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a2393bcb5780c7c532224b7f7c20f088/href">https://medium.com/media/a2393bcb5780c7c532224b7f7c20f088/href</a></iframe><p>Next, if we have a failed message we can send it to retry loop like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/67cce82959f45aa1ba20450d1bdc61ed/href">https://medium.com/media/67cce82959f45aa1ba20450d1bdc61ed/href</a></iframe><p>Keep in mind that we send to retry loop failed messages for both reasons: failing inside <strong>handler </strong>(because of reject OR exception) or failing because of <strong>connection issues </strong>(in such case AMQP will mark a message as <strong>redelivered</strong>). This is what happens in <em>sendMsgToRetry</em>: we acknowledge an original message, determine which attempt it is and send a new message (clone) to the next retry loop.</p><p>You can find a full implementation with descriptive comments <a href="https://gist.github.com/bondvt04/029342a51c5fc03da3e809dbc2be5edc">here</a>.</p><p>Also, you can visit <a href="https://forma-pro.com/blog/"><strong>our corporate blog</strong></a> to learn more about up-to-date IT trends and download some useful insights.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1f82c6afc1a1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/rabbitmq-retries-on-node-js-1f82c6afc1a1">RabbitMQ retries on Node.JS</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Docker setup for InfluxDB, Chronograf, Kapacitor, and PHP]]></title>
            <link>https://medium.com/formapro/docker-setup-for-influxdb-chronograf-kapacitor-and-php-d622535a41b?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/d622535a41b</guid>
            <category><![CDATA[influxdb]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[iot]]></category>
            <dc:creator><![CDATA[Max Kotliar]]></dc:creator>
            <pubDate>Thu, 01 Mar 2018 13:36:02 GMT</pubDate>
            <atom:updated>2020-04-29T20:06:47.754Z</atom:updated>
            <content:encoded><![CDATA[<p>I’ve recently consulted an IoT related project, where I suggested using InfluxData’s Services:</p><ul><li><a href="https://docs.influxdata.com/influxdb/v1.4/">InfluxDB</a> is open source time series database designed to handle high write and query loads.</li><li><a href="https://docs.influxdata.com/chronograf/v1.4/">Chronograf</a> is open source web application for infrastructure monitoring, alert management, data visualization, and database management.</li><li><a href="https://docs.influxdata.com/kapacitor/v1.4/">Kapacitor</a> is open source data processing framework that makes it easy to create alerts, run ETL jobs, and detect anomalies.</li></ul><p>I’d like to share my findings on how to setup them locally using Docker and how to use it from PHP.</p><p>Luckelly, InfluxData provides us with good pre-build images. We just have to properly configure and link the services.</p><p>In a shell, open an empty directory and create a docker-compose.yml file there, as follows:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0e9820b2b73bf1fb39e92c553a9578c7/href">https://medium.com/media/0e9820b2b73bf1fb39e92c553a9578c7/href</a></iframe><p>To start containers execute:</p><pre>docker-compose up -d</pre><p>Open <a href="http://localhost:8888/">http://localhost:8888</a> to access Chronograf UI.</p><p>Now, let’s install <a href="https://packagist.org/packages/influxdb/influxdb-php">influxdb/influxdb-php</a> which we use to query and store points:</p><pre>composer req <a href="https://packagist.org/packages/influxdb/">influxdb/</a>influxdb-php:^1</pre><p>Here’s the script that sends two points to InfluxDB:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/294115393f71800f0b9bcf3591dacce3/href">https://medium.com/media/294115393f71800f0b9bcf3591dacce3/href</a></iframe><p>To run it open the browser and type <a href="http://localhost/">http://localhost</a>:80.</p><p>As you can see the whole process was smooth and took just a few minutes. Having this working you are ready to explore great features of InfluxDB, Kapacitor. Have fun!</p><p>You can also visit <a href="https://forma-pro.com/blog/"><strong>our corporate blog</strong></a> to learn more about up-to-date IT trends and download some useful insights.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d622535a41b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/docker-setup-for-influxdb-chronograf-kapacitor-and-php-d622535a41b">Docker setup for InfluxDB, Chronograf, Kapacitor, and PHP</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Queue Interop roadmap.]]></title>
            <link>https://medium.com/formapro/queue-interop-roadmap-1f57c7dec827?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/1f57c7dec827</guid>
            <category><![CDATA[php]]></category>
            <category><![CDATA[symfony]]></category>
            <category><![CDATA[enqueue]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Max Kotliar]]></dc:creator>
            <pubDate>Wed, 28 Feb 2018 12:40:58 GMT</pubDate>
            <atom:updated>2018-03-15T07:45:02.371Z</atom:updated>
            <content:encoded><![CDATA[<p>The <a href="https://github.com/queue-interop/queue-interop">Queue Interop</a> project started in the summer of 2016, almost two years ago. Since then It has been well-tried by many developers. A lot of companies use it in production.</p><p>It is time to settle a date of a stable release and possibly some intermediate steps towards it:</p><ul><li>The 1.0 will be released in December 2018.</li><li>The 1.0-beta1 will be released in August 2018.</li><li>The <a href="https://github.com/queue-interop/queue-interop/releases/tag/1.0.0-alpha1">1.0-alpha1</a> will be released in March 2018.</li></ul><p>I don’t expect any serious changes or BC breaks. I’d be happy to gather as much feedback as possible during the next months prior to the release.</p><p>I call for feedback and comments. Feel free to open <a href="https://github.com/queue-interop/queue-interop/issues/new">an issue</a> to provide it.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1f57c7dec827" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/queue-interop-roadmap-1f57c7dec827">Queue Interop roadmap.</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PHP AMQP clients. Benchmark them all!]]></title>
            <link>https://medium.com/formapro/php-amqp-clients-benchmark-them-all-8a4e6adb1a6b?source=rss----fbd3ad5ede94---4</link>
            <guid isPermaLink="false">https://medium.com/p/8a4e6adb1a6b</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[enqueue]]></category>
            <category><![CDATA[rabbitmq]]></category>
            <dc:creator><![CDATA[Max Kotliar]]></dc:creator>
            <pubDate>Wed, 20 Dec 2017 12:34:11 GMT</pubDate>
            <atom:updated>2017-12-20T12:34:11.061Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/450/1*kAyK0H5TbNt3FUyh6PWGXA.png" /></figure><p>Increasing popularity of <a href="https://github.com/queue-interop/queue-interop#amqp-interop">AMQP interop</a> prompted a wave of questions regarding which adapter is the fastest. In this post, I am going to share my findings of message publishing performance. There will be a similar post about consumption side.</p><ul><li><a href="https://github.com/php-amqplib/php-amqplib">php-amqplib</a></li><li><a href="https://github.com/pdezwart/php-amqp">amqp-ext</a></li><li><a href="https://github.com/jakubkulhan/bunny">bunny</a></li></ul><p>as well as their interop adapters:</p><ul><li><a href="https://github.com/php-enqueue/enqueue-dev/blob/master/docs/transport/amqp_lib.md">enqueue/amqp-lib</a></li><li><a href="https://github.com/php-enqueue/enqueue-dev/blob/master/docs/transport/amqp.md">enqueue/amqp-ext</a></li><li><a href="https://github.com/php-enqueue/enqueue-dev/blob/master/docs/transport/amqp_bunny.md">enqueue/amqp-bunny</a></li></ul><p>and one from Golang world:</p><ul><li><a href="https://github.com/streadway/amqp">streadway/amqp</a></li></ul><p>We are going to compare performance of the libraries, the cost of an interop wrappers. It would also be also interesting to compare PHP implementations with something from another world to feel the difference. I chose streadway/amqp Go’s library which seems like widely used one.</p><p>A quick note on methodology. I ran benchmarks on DigitalOcean droplet (1Gb RAM, 1 CPU, Ubuntu 16.04 x64) inside docker containers. Every run was done in new containers. The benchmarks source code could be found <a href="https://github.com/php-enqueue/enqueue-dev/pull/298">here</a>.</p><p>A picture is worth a thousand words so here’s some statistics:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*k6tczEH9wwCespNY7PCHZA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*x0EwmWsVwm3sTK2Epw1VWQ.png" /></figure><h4>Check for yourself</h4><p>You can run benchmarks yourself. For that, you have to download enqueue dev repository, do composer install and run bench script. Like this:</p><pre>git clone <a href="https://github.com/php-enqueue/enqueue-dev.git">https://github.com/php-enqueue/enqueue-dev.git</a><br>composer install<br>BODY_SIZE=10000 ./bin/bench bench/AmqpExtBench.php --report=aggregate</pre><h4>Conclusion</h4><ul><li>amqp-ext is the fastest among PHP implementations.</li><li>bunny is almost as fast as the AMQP extension.</li><li>php-amqplib is sometimes two times slower than other solutions.</li><li>Interestingly Go script was the real winner only for a really small payload. For the rest it showed a bit better result than amqp-ext. It should’ve been much better IMO.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8a4e6adb1a6b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/formapro/php-amqp-clients-benchmark-them-all-8a4e6adb1a6b">PHP AMQP clients. Benchmark them all!</a> was originally published in <a href="https://medium.com/formapro">formapro</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>