<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Redash</title>
    <description>The latest articles on DEV Community by Redash (@redash).</description>
    <link>https://dev.to/redash</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F451%2F0b83138a-5e2f-4913-988b-f1d110033258.png</url>
      <title>DEV Community: Redash</title>
      <link>https://dev.to/redash</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/redash"/>
    <language>en</language>
    <item>
      <title>How We Spotted and Fixed a Performance Degradation in Our Python Code</title>
      <dc:creator>Omer Lachish</dc:creator>
      <pubDate>Tue, 05 Nov 2019 07:27:14 +0000</pubDate>
      <link>https://dev.to/redash/how-we-spotted-and-fixed-a-performance-degradation-in-our-python-code-4g5l</link>
      <guid>https://dev.to/redash/how-we-spotted-and-fixed-a-performance-degradation-in-our-python-code-4g5l</guid>
      <description>&lt;p&gt;Recently we’ve started transitioning from using &lt;a href="http://www.celeryproject.org/"&gt;Celery&lt;/a&gt; to using &lt;a href="https://python-rq.org/"&gt;RQ&lt;/a&gt; as our task running engine. For phase one, we only migrated the jobs that aren’t directly running queries. These jobs include things like sending emails, figuring out which queries need to be refreshed, recording user events and other maintenance jobs.&lt;/p&gt;

&lt;p&gt;After deploying this we noticed our RQ workers required significantly more CPU to perform the same amount of tasks as our Celery workers did. I thought I’d share how I profiled this and remedied the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  A word about the differences between Celery and RQ
&lt;/h2&gt;

&lt;p&gt;Both Celery and RQ have the concept of a Worker process and both use forking to allow parallelized execution of jobs. When you launch a Celery worker it forks to several different processes, each one autonomously handles tasks. With RQ, a worker will only instantiate a single sub-process (known as a “Work Horse”) which will perform a single job and then die. When the worker fetches another job from the queue, it will fork a new Work Horse.‌&lt;/p&gt;

&lt;p&gt;‌In RQ you can achieve the same parallelism as Celery simply by running more worker processes. However there is a subtle difference between Celery and RQ: Celery workers instantiate multiple subprocesses at launch time and reuse them for multiple tasks. With RQ, you have to fork on every job. There are pros and cons to both approaches, but these are out of scope for this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarking
&lt;/h2&gt;

&lt;p&gt;‌Before I profiled anything, I wanted a benchmark of how long it takes for a worker container to process 1000 jobs. I decided to focus on the &lt;code&gt;record_event&lt;/code&gt; job since it is a frequent, lightweight operation. I used the &lt;code&gt;time&lt;/code&gt; command to measure performance, which required a couple changes to the source code:‌&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To measure these 1000 jobs I preferred RQ’s burst mode, which exits the process after handling the jobs.&lt;/li&gt;
&lt;li&gt;I wanted to avoid measuring other jobs that might be scheduled at the time of benchmarking. So I moved &lt;code&gt;record_event&lt;/code&gt; to a dedicated queue called &lt;code&gt;benchmark&lt;/code&gt; by replacing &lt;code&gt;@job(‘default’)&lt;/code&gt; with &lt;code&gt;@job(‘benchmark’)&lt;/code&gt; right above &lt;code&gt;record_event&lt;/code&gt;’s declaration in &lt;code&gt;tasks/general.py&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we can start timing things. First of all, I wanted to see how long it takes for a worker to start and stop (without any jobs) so I can subtract that time from any result later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose exec worker bash -c "time ./manage.py rq workers 4 benchmark"

real    0m14.728s
user    0m6.810s
sys 0m2.750s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;‌Worker initialization takes 14.7 seconds on my machine. I’ll remember that.‌ Then, I shoved 1000 dummy &lt;code&gt;record_event&lt;/code&gt; jobs onto the benchmark queue:‌&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose run --rm server manage shell &amp;lt;&amp;lt;&amp;lt; "from redash.tasks.general import record_event; [record_event.delay({ 'action': 'create', 'timestamp': 0, 'org_id': 1, 'user_id': 1, 'object_id': 0, 'object_type': 'dummy' }) for i in range(1000)]"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;‌Now let’s run the same worker command as before and see how long it takes to process 1,000 jobs:‌&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose exec worker bash -c "time ./manage.py rq workers 4 benchmark"

real    1m57.332s
user    1m11.320s
sys 0m27.540s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Deducting our 14.7 boot time, we see that it took 102 seconds for 4 workers to handle 1,000 jobs. Now let’s try to figure out why! For that, we’ll use &lt;code&gt;py-spy&lt;/code&gt; while our workers are working hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profiling
&lt;/h2&gt;

&lt;p&gt;Let’s add another 1,000 jobs (because our last measurement consumed all of them), run the workers and simultaneously spy on them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose run --rm server manage shell &amp;lt;&amp;lt;&amp;lt; "from redash.tasks.general import record_event; [record_event.delay({ 'action': 'create', 'timestamp': 0, 'org_id': 1, 'user_id': 1, 'object_id': 0, 'object_type': 'dummy' }) for i in range(1000)]"
$ docker-compose exec worker bash -c 'nohup ./manage.py rq workers 4 benchmark &amp;amp; sleep 15 &amp;amp;&amp;amp; pip install py-spy &amp;amp;&amp;amp; rq info -u "redis://redis:6379/0" | grep busy | awk "{print $3}" | grep -o -P "\s\d+" | head -n 1 | xargs py-spy record -d 10 --subprocesses -o profile.svg -p'
$ open -a "Google Chrome" profile.svg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I know, that last command is quite a handful. Ideally I would break that command on every ‘&amp;amp;&amp;amp;’ for readability, but the commands should run sequentially inside the same &lt;code&gt;docker-compose exec worker bash&lt;/code&gt; session, so here’s a quick breakdown of what it does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start 4 burst workers in the background&lt;/li&gt;
&lt;li&gt;Wait 15 seconds (roughly to get them to finish booting)&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;py-spy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;rq-info&lt;/code&gt; and slice up the pid for one of the workers&lt;/li&gt;
&lt;li&gt;Record 10 seconds of activity in that pid and save it to &lt;code&gt;profile.svg&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result was this flame graph:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yaA5mOfK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/-lffkrC85f44h7ixlMyBVVIqB2j-KZOLSeRoDu4MyTG28CYh3uKgMEJ_4iniEze9Gyee9cm6NCvT-07-sHvyBZw-zuKrOPNsMTqLN5UGTWjs25yl-S1fEedPep8BBSK6kxGfvZE" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yaA5mOfK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/-lffkrC85f44h7ixlMyBVVIqB2j-KZOLSeRoDu4MyTG28CYh3uKgMEJ_4iniEze9Gyee9cm6NCvT-07-sHvyBZw-zuKrOPNsMTqLN5UGTWjs25yl-S1fEedPep8BBSK6kxGfvZE" alt="Flame graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Digging inside the flame graph I noticed that record_event spends a big portion of its execution time in &lt;code&gt;sqlalchemy.orm.configure_mappers&lt;/code&gt; and it happens on every job execution. From &lt;a href="https://docs.sqlalchemy.org/en/13/orm/mapping_api.html#sqlalchemy.orm.configure_mappers"&gt;their docs&lt;/a&gt; I saw this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Initialize the inter-mapper relationships of all mappers that have been constructed thus far.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This sort of thing really doesn’t need to happen on every fork. We could initialize these relationships in the parent worker once and avoid the repeated effort in the work horses.&lt;/p&gt;

&lt;p&gt;‌So &lt;a href="https://github.com/getredash/redash/pull/4314"&gt;I’ve added a call&lt;/a&gt; to &lt;code&gt;sqlalchemy.org.configure_mappers()&lt;/code&gt; before starting the work horse and measured again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose run --rm server manage shell &amp;lt;&amp;lt;&amp;lt; "from redash.tasks.general import record_event; [record_event.delay({ 'action': 'create', 'timestamp': 0, 'org_id': 1, 'user_id': 1, 'object_id': 0, 'object_type': 'dummy' }) for i in range(1000)]
$ docker-compose exec worker bash -c "time ./manage.py rq workers 4 benchmark"

real    0m39.348s
user    0m15.190s
sys 0m10.330s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we deduct our 14.7 second boot time, we are down from 102 seconds to 24.6 seconds for 4 workers to handle 1000 jobs. That’s a 4x improvement! With this fix we managed to slice our RQ production resources by 4 and keep the same throughput.‌&lt;/p&gt;

&lt;p&gt;My key takeaway here is that you should keep in mind that your app behaves differently when it’s a single process and when it forks. If there’s some heavy lifting to perform on every job, it’s usually a good idea to do it once before the fork. These kinds of things don’t show up during testing and development, so make sure you measure well and dig into any performance issues that come up.&lt;/p&gt;

</description>
      <category>python</category>
      <category>performance</category>
    </item>
    <item>
      <title>What’s ahead for Redash in 2019 🤩</title>
      <dc:creator>Arik Fraimovich</dc:creator>
      <pubDate>Wed, 16 Jan 2019 10:51:34 +0000</pubDate>
      <link>https://dev.to/redash/whats-ahead-for-redash-in-2019--2998</link>
      <guid>https://dev.to/redash/whats-ahead-for-redash-in-2019--2998</guid>
      <description>&lt;p&gt;When I wrote &lt;a href="https://medium.com/@arikfr/the-journey-from-side-project-to-open-source-company-taking-the-first-step-8e8259ac80cb"&gt;the blog post announcing the founding of the Redash company&lt;/a&gt;, I was naive enough to believe that I would write regular updates about the process of building Redash as a product and a company. Silly me. Turns out it's a full-time job and then some to build a product, bootstrap a company and grow a team. Something had to give; and one such thing was regular updates about the process. So much has happened by now that one article could hardly cover it all. But with 2018 recently concluded, here is my attempt to summarize the last twelve months and take a peek at what's to come.&lt;/p&gt;

&lt;p&gt;On its surface, Redash's trajectory in 2018 went like previous years: we added users and customers, grew our revenues, and made the product better than ever. But there was one big difference as we entered 2018: Redash became a team effort.&lt;/p&gt;

&lt;p&gt;Until late-2017, Redash was a one-man operation. It became a group of three at the start of 2018. And now for 2019 there are six of us. What enabled this team growth was our customers. As before, we haven’t taken any external funding. To all our past and present customers: &lt;strong&gt;thank you 🙏&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;While we started as a team of 3 in 2018, we are starting 2019 as a team of 6. What enabled this team growth was our customers.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As Redash matured as a company, the product also matured dramatically in 2018. We had three major releases (&lt;a href="https://medium.com/r/?url=https%3A%2F%2Fblog.redash.io%2Fredash-v4-is-out-36c11fe0c682"&gt;v4&lt;/a&gt;, &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fblog.redash.io%2Fredash-v5-is-out-1d9a3d93c20a"&gt;v5&lt;/a&gt; and &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fblog.redash.io%2Fjust-in-time-for-christmas-redash-v6-70cb23dfbbf3"&gt;v6&lt;/a&gt;) that included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New UI and better UX&lt;/li&gt;
&lt;li&gt;Dynamic dashboard layouts&lt;/li&gt;
&lt;li&gt;Tagging and Favorites&lt;/li&gt;
&lt;li&gt;Parameters UI improvements&lt;/li&gt;
&lt;li&gt;More visualizations&lt;/li&gt;
&lt;li&gt;More data sources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1I2N-qG7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/1%2AWYVzC1Kmgia6OzFSlM_h5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1I2N-qG7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/1%2AWYVzC1Kmgia6OzFSlM_h5w.png" alt="New Dashboards UX introduced in V4"&gt;&lt;/a&gt;&lt;br&gt;A glimpse of the new UI introduced in V4.
  &lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Plans for 2019&lt;/strong&gt;
&lt;/h1&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Team Effort&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As the team grows, we're building procedures that keep Redash a fun and productive place to work and (importantly) make &lt;strong&gt;me&lt;/strong&gt; redundant to the day to day operations of the company. This will improve team velocity, promote greater stability for our customers, and free my time to focus on more strategic projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Even more interactive dashboards and queries&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In the releases we made in 2018, we improved the UI for parameters significantly. In 2019 we’re going to finish this effort by adding a few more needed capabilities to parameters (optional parameters, multiple select, better support in dashboards) and by making parameters available everywhere (read only users, shared dashboards, embeds).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Better Permissions Model&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once we have the ability to safely run parameterized queries, we can finally upgrade the Redash permission model and make it more user friendly. The goal is to augment the current model (if you have access to the data source, you have access to the query/dashboard) with a Google Drive like model, where you can assign permissions to individual users or groups on the content level.&lt;/p&gt;

&lt;p&gt;Coupled with the improved parameters support mentioned above, Redash in 2019 will reach further and empower more people inside your organizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Technical Progress&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This year, we plan to conclude our migration to React that began in 2018. This should make it  easier to add features and interface with our open-source community. We will also continue to improve the testing story around Redash (thanks Cypress and Percy!). All this will allow us to release more often with greater confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Community&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We recently invited nine members from the user community to become Redash maintainers. In 2019, we want to improve our coordination with maintainers and clean up our backlog of Pull Requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;More Guides and Resources&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One thing we neglected in 2018 was to produce up-to-date documentation and usage examples. We want to change this in 2019, but we would also appreciate your feedback! If you have any interesting uses of Redash, we urge you to share them. &lt;a href="https://medium.com/r/?url=http%3A%2F%2Fdiscuss.redash.io"&gt;The forum&lt;/a&gt; is a great venue for this.&lt;/p&gt;




&lt;p&gt;These are our plans &lt;em&gt;for now&lt;/em&gt;. It’s known that no plan survives first contact, so we hope to still surprise you (and ourselves 😅) with some of what we will achieve in 2019. 🍾&lt;/p&gt;

</description>
      <category>yearinreview</category>
      <category>2018</category>
      <category>2019</category>
    </item>
  </channel>
</rss>
