<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.2.2">Jekyll</generator><link href="https://tomfern.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://tomfern.com/" rel="alternate" type="text/html" /><updated>2026-01-06T16:15:38+00:00</updated><id>https://tomfern.com/feed.xml</id><title type="html">Chaos is order undeciphered</title><author><name>Tommy Fernandez</name></author><entry><title type="html">Better Markdown for Writers</title><link href="https://tomfern.com/post/writing-in-markdown" rel="alternate" type="text/html" title="Better Markdown for Writers" /><published>2023-01-01T03:00:00+00:00</published><updated>2023-01-01T03:00:00+00:00</updated><id>https://tomfern.com/post/writing-with-markdown</id><content type="html" xml:base="https://tomfern.com/post/writing-in-markdown"><![CDATA[<p>TLDR; you can find the templates I use for writing at my <a href="https://github.com/TomFern/writing-templates">writing repository</a>.</p>

<hr />

<p>My <a href="/post/what-got-me-writing">very first writings</a> were done in Org-mode for Emacs. <a href="https://orgmode.org/">org-mode</a> is a beat of a format for the Emacs text editor. With it, you can take notes, track time, organize projects, make to-do lists, and write blog posts.</p>

<p>I loved working with org-mode. But, since no one uses it, I had to convert my lovely org files into Markdown or Word to get a review or share a publishable package.</p>

<p>The conversion itself was fine. <a href="https://pandoc.org/">Pandoc</a> is quite capable of parsing org files. The nag was that I had two juggle org and Markdown formats in my head. Too many times, I found myself adding org directives in Markdown and wondering why it didn’t work. Eventually, I decided to ditch org and move to Markdown for good.</p>

<h2 id="org-mode-includes">Org-mode includes</h2>

<p>The thing I loved most about org (for writing, at least) was its ability to use includes. I can add an <code class="language-plaintext highlighter-rouge">#+INCLUDE</code> directive, and Emacs/pandoc would expand the contents of that file into the main document. For example, with org, I could do this:</p>

<pre><code class="language-org-mode">* My Title

Check out this awesome code:

#+INCLUDE: "./example.js" src javascript
</code></pre>

<p>Having code in separate files makes it very easy to copy/paste the text into tools like Grammarly or Hemingway Editor.</p>

<h2 id="adding-includes-to-markdown">Adding includes to Markdown</h2>

<p>Markdown, unfortunately, does not have includes. With meant that I had to mix code and text on the same file. I didn’t want to do that. Was there any way of adding includes to Markdown?</p>

<p>After some Googling around, I found <a href="https://www.gnu.org/software/m4/">m4</a>, a macro processor that has an <a href="https://www.gnu.org/software/m4/manual/html_node/Include.html">include directive</a>. To use this feature on Markdown, we must first change the default quotation marks by adding this at the top of the main Markdown file:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>changequote(`{{', `}}')
</code></pre></div></div>

<p>This enables me to write my code sample on a separate file, e.g., <code class="language-plaintext highlighter-rouge">example.js</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>```javascript
console.log("Hey!")
```
</code></pre></div></div>

<p>And expand its contents by adding this line in my main Markdown file:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>include({{example.js}})
</code></pre></div></div>

<p>To render the expanded Markdown, we call m4 like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>m4 <span class="nt">-E</span> <span class="nt">-I</span>./ main-post.md <span class="o">&gt;</span> expanded-post.md
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">-I</code> argument points to the include folder. So, this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>changequote(`{{', `}}')

# My Title

Check out this awesome code:

include({{example.js}})
</code></pre></div></div>

<p>Renders as:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># My Title

Check out this awesome code:

```javascript
console.log("Hey!")
```
</code></pre></div></div>

<h2 id="putting-everything-together">Putting everything together</h2>

<p>Over time, I put together small Makefiles that use this technique in conjunction with pandoc to convert my flavor of Markdown into plain Markdown, Word, or plain text. You can check the templates I use in my <a href="https://github.com/TomFern/writing-templates">writing repository</a>.</p>

<p>Happy writing!</p>

<p>-Tommy</p>]]></content><author><name>Tommy Fernandez</name></author><category term="writing" /><summary type="html"><![CDATA[This is how I organize my writing projects.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://tomfern.com/images/gutenberg.jpg" /><media:content medium="image" url="https://tomfern.com/images/gutenberg.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">My Writing Process</title><link href="https://tomfern.com/post/my-writing-process" rel="alternate" type="text/html" title="My Writing Process" /><published>2022-07-09T11:00:00+01:00</published><updated>2022-07-09T11:00:00+01:00</updated><id>https://tomfern.com/post/writing-process</id><content type="html" xml:base="https://tomfern.com/post/my-writing-process"><![CDATA[<p>I tend to have a more <em>suis generis</em> process for writing technical documents. With this I mean I don’t have too much of a structure when it’s time to type. I usually do an outline first (but not always) and then write, edit, cut, redit, recut until I have something that’s, at the very least, decent.</p>

<p>But lately I’ve been paying more attention to how I write and I’ve noted that I’m kind of following a process after all. After some though, I think I’m ready to formalize it a bit. For now, I’ll only name the steps:</p>

<ul>
  <li>Research</li>
  <li>Sketch</li>
  <li>1st draft</li>
  <li>2nd draft</li>
  <li>3rd draft</li>
  <li>Review</li>
  <li>Publish</li>
</ul>

<p>Let me explain what happens in each one.</p>

<h2 id="research">Research</h2>

<p>Any good article starts from good knowledge. This is probably my favorite part of the process as I don’t really need to worry about the writing. I just can learn interesting.</p>

<p>During this process I take notes, collect links, grab interesting paragraphs, see videos and presentations, save screenshots and try to learn a little bit of the people behind the tech (an often forgotten part of technical writing).</p>

<p>I keep track of everything with <a href="https://www.notion.so/">Notion</a>.</p>

<p>Once I think I know enough get started with the article is sketching time.</p>

<h2 id="sketch">Sketch</h2>

<p>During this phase I create a tentative outline: write headers, describe the contents of each section, make notes of there diagrams will be needed and plan the ideas. I write all my articles in Markdown and use <a href="https://pandoc.org/">pandoc</a> if I need to switch formats.</p>

<p>Before I start writing paragraphs I like to place a single sentence descring its main idea. It helps me keep focused and form an idea of how the article will flow from paragraph to paragran and section to section.</p>

<p>For most of the first phases of the writing <a href="https://tomfern.com/posts/vim-for-writers/">I use Vim as my primary text editor</a>, because it’s so easy to move large chunks of text around.</p>

<h2 id="1st-draft">1st draft</h2>

<p>The first draft is usually the hardest. If I’m lucky the work I did on the sketch will help me get over the first draft soon.</p>

<p>In this phase, guided by the ideas/paragraph flow, I flesh out each paragragh. I also design most diagrams, write their alt and caption texts. I do this here because a diagram with a good caption can save a lot of writing.</p>

<p>The first draft doesn’t have to be perfect, it rarely is any good in fact. The important part is fill the empty space with something to work with.</p>

<h2 id="2nd-draft">2nd draft</h2>

<p>On the second draft I want to make sure:</p>
<ul>
  <li>I haven’t missed something important.</li>
  <li>I’m writing accurately.</li>
  <li>I haven’t gone any rabbit holes.</li>
</ul>

<p>So I go back to my reseach notes and compare what I’ve written with the facts. Having written about the topic I can understand my notes more deeply.</p>

<p>I add any missing details, do a little fact checking (usually do a more research), take out parts that aren’t needed.</p>

<p>By the second draft, I expect to have a more coherent article. So I go streamlining the text paragraph by paragraph section by section. I use <a href="https://grammarly.com/">Grammarly</a> heavily at this point to correct and consolidate. I love Grammarly, its suggestions are usually spot on. Although, it’s a bit stubborn at times regarding word choices. There’s a lot of “this word is overused, use this one instead” that really makes no sense. But overall is a great tool.</p>

<h2 id="3rd-draft">3rd draft</h2>

<p>By the third draft I can take a more panoramic view of the draft. I check if it flows well and make little adjustments. For that, I switch to <a href="https://typora.io/">Typora</a> as it gives me the full rendering of the page with graphics.</p>

<p>I’m a strong believer of simple language so during this phase I use <a href="https://hemingwayapp.com/">Hemingway Editor</a> to simplify complex sentences. In all honesty I wish I could write simple sentences from the get go. It would simplify things so much. Regardless, the Hemingway editor is great. It highlights complex sentences, and offers a few simpler alternative words.</p>

<p>I tend to rewrite a lot of paragraphs during this last draft. When I’m out of ideas or have difficulty finding different ways of expressing a though I use <a href="https://quillbot.com/">Quillbot</a>, a rephrasing engine, to get altenate versions and inspiration.</p>

<p>Before sending the draft to review do a pass with Google Docs. It’s surprising how good this humble word editor is at picking errors no other tool had.</p>

<h2 id="review">Review</h2>

<p>I’m blesseed with having people who can review my draft. Doubly blessed with a technical reviewer that fact checks and offers insightful suggestions, and a proofreader that doesn’t let me get away with lazy writing. After the review process, the draft has reached a new level in quality.</p>

<h2 id="publish">Publish</h2>

<p>The last stretch involves preparing the draft for publication. I check that graphics all looking good, code is lined up, links are broken and everything is ready for publication.</p>

<p>Once published, it’s rinse and repeat with the next draft.</p>

<h2 id="recap">Recap</h2>

<p>Here’s how the process looks visually.</p>

<p><img src="/images/writing-process.jpg" alt="The phases with a circular arrow mean the steps inside are to be repeated until ready to move on." /></p>

<p>Happy writing</p>

<p>-Tommy</p>]]></content><author><name>Tommy Fernandez</name></author><category term="writing" /><summary type="html"><![CDATA[The process I use from research to publication in my work as a full-time technical writer]]></summary></entry><entry><title type="html">Always use ImagePullSecrets</title><link href="https://tomfern.com/post/always-use-imagepullsecrets" rel="alternate" type="text/html" title="Always use ImagePullSecrets" /><published>2020-11-14T10:00:00+00:00</published><updated>2020-11-14T10:00:00+00:00</updated><id>https://tomfern.com/post/always-use-imagepullsecrets</id><content type="html" xml:base="https://tomfern.com/post/always-use-imagepullsecrets"><![CDATA[<p><img src="/images/speed-limit.jpg" alt="Speed Limit" /></p>

<p>Docker Hub <a href="https://www.docker.com/increase-rate-limits">introduced rate limits</a> for image pulls earlier this month. Being the default registry for Docker images, this is one of the biggest in the ecosystem in the last few years, and it will affect not only Docker users but also Kubernetes setups.</p>

<h2 id="the-limits">The limits</h2>

<p>The new limits affect free users. No more unlimited free pulls, the party is over.</p>

<ul>
  <li><strong>Anonymous free users</strong>: 100 container pulls every 6 hours.</li>
  <li><strong>Authenticated free users</strong>: 200 container pulls every 6 hours.</li>
  <li><strong>Paid accounts</strong>: unlimited.</li>
</ul>

<p>At first, 100 pulls may sound like a lot. But think what happens when you start a Kubernetes deployment. Each pod pulls independently: if you spin up a ReplicaSet with 100 replicas, that’s 100 pulls at once.</p>

<p>The same thing happens when we’re doing a rolling update, a rollback, a canary release, or a blue-green deployment. As pods keep spawning, the chance of hitting the limit increases, and we face the most annoying of problems: random, unexpected, avoidable failures.</p>

<h2 id="what-to-do">What to do?</h2>

<p>What can you do? To see if you’re affected, check if you’re pulling images from Docker Hub with the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods <span class="nt">--all-namespaces</span> <span class="nt">-ojson</span> | jq <span class="nt">-r</span> <span class="s1">'.items[].spec | .containers[] // [] += .initContainers[] // [] | .image'</span> | <span class="nb">sort</span> <span class="nt">-u</span>
</code></pre></div></div>

<p>If you see images with no registry (just the image name) or where the registry is <code class="language-plaintext highlighter-rouge">docker.io</code>, you’re pulling from Docker Hub, so keep reading.</p>

<p>Next, check if you’re using authentication. All images should be pulled with authentication. Anonymous pulls are rate limited by IP. Even if you don’t need a lot of pulls, you may still be affected by other users sharing the network.</p>

<p>The bright side is that adding authentication is really easy. It only takes two steps. Here’s how to do it.</p>

<h3 id="step-1-tell-kubernetes-how-to-authenticate">Step 1: tell Kubernetes how to authenticate</h3>

<p>The first step is to add your Docker Hub username and password to the cluster. We can do this with kubectl. You only have to do this once per cluster.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl create secret docker-registry my-dockerhub-account <span class="se">\</span>
    <span class="nt">--docker-server</span><span class="o">=</span>docker.io <span class="se">\</span>
    <span class="nt">--docker-username</span><span class="o">=</span>DOCKER_HUB_USERNAME <span class="se">\</span>
    <span class="nt">--docker-password</span><span class="o">=</span>DOCKER_HUB_PASSWORD
</code></pre></div></div>

<h3 id="step-2-add-imagepullsecrets-in-the-deployment">Step 2: add ImagePullSecrets in the deployment</h3>

<p>Once the secret is safely stored in Kubernetes, we only need to mention it on the deployment manifest. For this, add <code class="language-plaintext highlighter-rouge">imagePullSecrets</code> in the <code class="language-plaintext highlighter-rouge">spec.template.spec</code> tree:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="na">imagePullSecrets</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">my-dockerhub-account</span>
</code></pre></div></div>

<p>For example, if I want to run a deployment with three <a href="https://www.nginx.com/">nginx</a> pods, the end result would be:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">nginx-deployment</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">nginx</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">nginx</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">nginx</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">imagePullSecrets</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">my-dockerhub-secret</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">nginx</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">nginx:1.14.2</span>
        <span class="na">ports</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">80</span>
</code></pre></div></div>

<p>That’s all you need. When you have the chance, <code class="language-plaintext highlighter-rouge">kubectl apply</code> the new manifest to start using authentication.</p>

<h2 id="better-safe-than-sorry">Better safe than sorry</h2>

<p>I hope this helps you to prevent avoidable errors. Once authenticated, you should have a more consistent experience and a higher limit.</p>

<p>I’ve not heard of anyone hitting the limit yet. Docker Hub said they’ll enforce them gradually, so I’m sure it’s a matter of time until someone gets bitten by the new caps.</p>]]></content><author><name>Tommy Fernandez</name></author><category term="kubernetes" /><category term="docker" /><summary type="html"><![CDATA[Kubernetes users: don't get bitten by rate limits.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://tomfern.com/images/speed-limit.jpg" /><media:content medium="image" url="https://tomfern.com/images/speed-limit.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Programming humans</title><link href="https://tomfern.com/post/programming-humans" rel="alternate" type="text/html" title="Programming humans" /><published>2020-11-03T10:00:00+00:00</published><updated>2020-11-03T10:00:00+00:00</updated><id>https://tomfern.com/post/programming-humans</id><content type="html" xml:base="https://tomfern.com/post/programming-humans"><![CDATA[<p><img src="/images/image-4.jpg" alt="" /></p>

<p>While working on the slides for the <a href="./talk-nerdearla-2020">talk I gave</a> a few weeks ago, it occurred to me that writing and coding have more than a few things in common. I originally planned to speak a bit about that but had to take it out for lack of space. I’ll make a post instead, this one.</p>

<h2 id="thinks-in-common">Thinks in common</h2>

<p><strong>Language</strong></p>

<p>Both writing and coding use a language. Writing happens in any of the hundreds of languages people speak all over the world. Coding is done in synthetic languages that are mostly inspired by English.</p>

<p><strong>Rules</strong></p>

<p>Both have rules. Speaking on general terms, coding has strict rules because the computer needs to parse it unequivocally—the developer’s intention must be defined with mathematical accuracy. Human languages also have rules, but they are looser, ever-changing, and don’t always make logical sense. Rules are lax because humans can interpret and make assumptions. We can use this extra bandwidth to communicate on a deeper level.</p>

<p><strong>Movement</strong></p>

<p>With this I say that both do something. Writing tells a story, shares an idea (like I’m doing now), or argues about an opinion. After reading, the reader may have a new thing to consider (even if they don’t agree) or know how to do something they didn’t know before. Whether as a book, as a script for a movie, or a screenplay, writing moves people.</p>

<p>The same goes for code. Programs move the world, and we wouldn’t be able to live here without them.</p>

<p><strong>Virtues</strong></p>

<p>Coding and writing also share some virtues. Simplicity and clarity come to mind—simple trumps complex in both writing and coding. Style and elegance are harder to define, but we know it when they see it.</p>

<h2 id="humans-have-minds">Humans have minds</h2>

<p>Coding is one of the most complicated fields anyone can get in these days. But at least computers don’t have their own minds (although, sometimes they seem to do).</p>

<p>Humans will pick and choose. They won’t read boring things. So not only do writers must be clear, but they also must pique the reader’s interest and give them something of value.</p>

<h2 id="language-the-io-of-the-mind">Language: the I/O of the mind</h2>

<p>Even though writing and coding touch on many points. Both activities need very different skillsets. Otherwise, playwrights would be the best software engineers, and all developers would be bestsellers authors (many are).</p>

<p>Like a clockmaker quietly putting together a watch, developers’ primary skills are knowledge and logic. Every piece of code must work in a very definite way, and there is no room for mistakes.</p>

<p>To me, a writer is a bit closer to a musician. They have more freedom, they have more options, but their prose must still be ruled by logic. It also—like a composer—must be guided by a kind of metric. One reads with the ear.</p>

<h2 id="salute-your-fellow-writer">Salute your fellow writer</h2>

<p>If you’re a dev, the next time you see a writer, give them a nod and a hat tip. You have more in common with them than you think.</p>

<p>-Tomas</p>]]></content><author><name>Tommy Fernandez</name></author><category term="writing" /><summary type="html"><![CDATA[Language is I/O of the mind]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://tomfern.com/images/image-4.jpg" /><media:content medium="image" url="https://tomfern.com/images/image-4.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Talk: Technical writing in English</title><link href="https://tomfern.com/post/talk-nerdearla-2020" rel="alternate" type="text/html" title="Talk: Technical writing in English" /><published>2020-10-21T11:00:00+01:00</published><updated>2020-10-21T11:00:00+01:00</updated><id>https://tomfern.com/post/nerdearla-2020-talk</id><content type="html" xml:base="https://tomfern.com/post/talk-nerdearla-2020"><![CDATA[<p><img src="/images/nerdearla.png" alt="" /></p>

<p>I gave a talk about technical writing at <a href="https://nerdear.la/">nerdear.la</a>, a bilingual convention about technology and people that took place October 2020 in Argentina.</p>

<h2 id="the-talk">The Talk</h2>

<p>The slides are in English, the talk is in Spanish. At the end of the talk, I’ll be answering questions.</p>

<iframe width="420" height="315" src="http://www.youtube.com/embed/1VKy65zqQ9E" frameborder="0" allowfullscreen=""></iframe>

<h2 id="slides">Slides</h2>

<p>You may download the slides <a href="https://drive.google.com/file/d/14Qo54DON9l2xhd1X6XyUl5_WGbImdmdl/view?usp=sharing">here</a>. You may use them or modify them, but if you do, please make the appropiate attribution.</p>

<p>-Tommy</p>]]></content><author><name>Tommy Fernandez</name></author><category term="talks" /><summary type="html"><![CDATA[View my talk 'Escritura técnica en inglés']]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://tomfern.com/images/nerdearla.png" /><media:content medium="image" url="https://tomfern.com/images/nerdearla.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">React Native and Expo</title><link href="https://tomfern.com/post/react-native-and-expo" rel="alternate" type="text/html" title="React Native and Expo" /><published>2020-09-02T00:46:37+01:00</published><updated>2020-09-02T00:46:37+01:00</updated><id>https://tomfern.com/post/react-native-expo</id><content type="html" xml:base="https://tomfern.com/post/react-native-and-expo"><![CDATA[<p>I’ve started my career as a backend developer. Pretty early on, I had to get familiar the console and chose Vim as my main editor. Afterwards, as a sysadmin and dba I lived in ssh sessions. It’s great, I love it, but when it’s time to switch to the frontend I struggle.</p>

<p>Imagine my hapiness when I found about a project that lets me code for mobile without leaving the command line.</p>

<h2 id="react-native-and-expo">React Native and Expo</h2>

<p>React Native is a Facebook project that converts Javascript into native Android and iOS code. Using one language for both platforms is definetly enticing and conventient.</p>

<p>Expo is an official React Native project that lets you do without full featured IDEs like Android Studio.</p>

<p>https://expo.io/</p>

<p>Curious about how it worked, I tried it for a few days and here’s the result.</p>

<h2 id="setting-up">Setting Up</h2>

<p>We only need Node.js to get started:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm <span class="nb">install</span> <span class="nt">-g</span> expo-cli
<span class="nv">$ </span>expo init
? Choose a template: <span class="o">(</span>Use arrow keys<span class="o">)</span>
<span class="nt">-----</span> Managed workflow <span class="nt">-----</span>
❯ blank                 a minimal app as clean as an empty canvas
blank <span class="o">(</span>TypeScript<span class="o">)</span>    same as blank but with TypeScript configuration
tabs                  several example screens and tabs using react-navigation
<span class="nt">-----</span> Bare workflow <span class="nt">-----</span>
minimal               bare and minimal, just the essentials to get you started
minimal <span class="o">(</span>TypeScript<span class="o">)</span>  same as minimal but with TypeScript configuration
<span class="nv">$ </span>npm <span class="nb">install</span>
</code></pre></div></div>

<!--
$ npm install --save-dev

$ npm install --save-dev jest
-->

<h2 id="running">Running</h2>

<p>Running the application is as easy as:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm run start
</code></pre></div></div>

<p>The command starts a bunch of processes, including a local dashboard running on <code class="language-plaintext highlighter-rouge">localhost:19002</code>.</p>

<p>The quickest way of viewing the application is by scanning the QR code with your phone and running it on the physical device.</p>

<p>Additionally, you can run the application in a virtual device if you have installed the emulator (Android Studio and avd)</p>

<!-- - Web?
-->

<h2 id="building">Building</h2>

<p>The Expo platform offers a cloud build service. Compiling an Android package is as easy as:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>expo build:android
</code></pre></div></div>

<p>After a few minutes, you’ll find the signed APK from the website, ready to upload to the Play Store.</p>

<h2 id="ejecting">Ejecting</h2>

<p>The Expo build service is nice but it can get inflexible. I’d like to be able to do End-to-End testing, and manage the project entirely in my CI/CD workflow instead of depending on 3rd parties.</p>

<p>Fortunately, Expo can create native Android and iOS projects that are will feel familiar to mobile developers.</p>

<p>To get these files, we must eject the project. The price we pay is that we no longer can build it from the Expo service:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>expo eject
? How would you like to eject your app?
Read more: https://docs.expo.io/versions/latest/expokit/eject/ <span class="o">(</span>Use arrow keys<span class="o">)</span>
❯ Bare: I<span class="s1">'d like a bare React Native project.
ExpoKit: I'</span>ll create or log <span class="k">in </span>with an Expo account to use React Native and the Expo SDK.
Cancel: I<span class="s1">'ll continue with my current project structure.
</span></code></pre></div></div>

<p>Of course, Expo may not be the best alternative for every case: https://docs.expo.io/versions/latest/introduction/why-not-expo/</p>

<p>The <code class="language-plaintext highlighter-rouge">android</code> directory can be opened by Android Studio. We can compile an APK with gradle and run the project in an emulator as usual.</p>

<h2 id="summary">Summary</h2>

<p>Expo is a nice alternative to full-featured IDEs. We can prototype pretty quickly a mobile project and when it’s time for more serious work. We can get a good Android project.</p>]]></content><author><name>Tommy Fernandez</name></author><category term="react" /><summary type="html"><![CDATA[Imagine my hapiness when I found about a project that lets me code for mobile without leaving the command line.]]></summary></entry><entry><title type="html">What Got Me Writing</title><link href="https://tomfern.com/post/what-got-me-writing" rel="alternate" type="text/html" title="What Got Me Writing" /><published>2020-08-08T11:00:00+01:00</published><updated>2020-08-08T11:00:00+01:00</updated><id>https://tomfern.com/post/what-got-me-writing</id><content type="html" xml:base="https://tomfern.com/post/what-got-me-writing"><![CDATA[<p><img src="/images/writing.jpg" alt="" /></p>

<p>When I started freelancing, I never imagined I would end up writing for a living. Never in a hundred years. Yet, I’ve been doing it full-time for almost a year now.</p>

<p>I got interested in freelancing because I wanted to try something different. I needed a break from the office routine. The job was OK. I had excellent colleagues and was friends with everyone. And while it wasn’t making me rich by any means, I never lacked for anything. It was a comfortable job—perhaps too much.</p>

<p>The trouble was that going to the office got harder and harder as time passed. It got to the point that I started sensing that the whole thing was a terrible waste of time. Meetings and busywork consumed most of my and everyone else’s workday. Going back and forth to the office four days a week (+1 home office day) took too long and squandered too much energy. At least that’s how I perceived it.</p>

<p>Now, don’t get me wrong, I know meetings and some degree of paperwork is unavoidable. I do. I’m fine with that. I made my peace with that; it’s just how companies work. But there is a point where bureaucracy takes over, and I felt we had long gone past it.</p>

<p>At first I thought that maybe I needed to change jobs. So I updated my resume, went to a few interviews, and even landed an offer that would have meant a substantial pay bump. In all honesty, when it was time to make the decision, I realized that I wasn’t too excited about the change—it felt like swapping one office for another. I realized that more money just wouldn’t cut it.</p>

<p>One friend who was already a freelancer recommended me to try Upwork. The way he described it sounded great, just what I needed. So I created a profile and started sending proposals for jobs I thought I could do.</p>

<p>Freelancing was a whole new and intimidating universe. It was exciting and scary. I did a few programming jobs here and there, I coded a chatbot, I did some database migrations.</p>

<p>One day I saw a job post asking for a 2000-word PHP installation tutorial. I knew PHP and MySQL pretty well, so I figured that it was easy money. By that time, I already had a personal blog with a few posts, and I thought that alone qualified me. So, without thinking too much about it, I sent my proposal. After all, how long could it take? Two thousand words… easy peasy, piece of cake… easy as falling off a log.</p>

<p>I landed the job and said I would have it in 2 days, 3 tops.</p>

<p>Boy, did I planning-fallacy-ed the thing…</p>

<p>I spend 2 entire days just writing down the steps and taking screenshots. Then 3 more writing the damn thing (all this while I did my daytime job). It dawned on me that proper writing is much harder than blogging for fun. It was exhausting, fun, and sleep-depriving. And when the draft got accepted, I was incredibly proud.</p>

<p>That was the point I started considering writing as a job.</p>

<p>In the following months, a lot of people were very kind and let me write for them. One person took a big chance and gave me enough work to take the plunge and try writing as a full-time job, for which I’ll be eternally grateful.</p>

<h2 id="so-what-i-like-about-writing">So, what I like about writing</h2>

<p><strong>It’s different</strong></p>

<p>Professional writing is different than any other job I had. For me, it’s the perfect mix of learning, playing, and working.</p>

<p><strong>It’s challenging</strong></p>

<p>Putting words on paper is easy. Making them make sense and tell a story, not so much. Finding the correct tone, showing just the right amount of information, and figuring out how best to deliver is a balancing act. It’s the kind of challenge that makes me want to keep trying harder.</p>

<p><strong>Give something back</strong></p>

<p>It feels good to contribute, even if it is only a grain of sand in the infinite expanse that is the Internet.</p>

<p><strong>View things in a different light</strong></p>

<p>Any text that doesn’t consider the audience is destined to fail. Writing forces me to put myself in other people’s shoes.</p>

<p><strong>Has its own pace</strong></p>

<p>There’s no on-call duty, no overtime, no meetings back to back, and (usually) no super-long hours. Within certain limits, I can choose how many hours I want to work.</p>

<p>The other side of the coin is that staying productive at a consistent pace it’s a lot more difficult. Some days I’m on fire. Others, I can’t write a word to save my life. And there is no way of knowing until I sit and try.</p>

<p><strong>Learn new things</strong></p>

<p>Writing is a way of learning. If you ask me, it’s one of the best ways. It has given me the chance to play with many wonderful technologies that I otherwise wouldn’t have paid attention to.</p>

<p><strong>Defrags the noggin</strong></p>

<p>Writing forces me to put things down and examine them in the critical light of the narrative. I frequently find that what made sense in the nebulous circumvolutions of the mind breaks down into a mess of disconnected sentences and repeated words as soon as I put them on the screen.</p>

<p><strong>Relearn old things</strong></p>

<p>We’ve all been tripped by an innocent question while explaining something that we thought we knew well.
Without fail, writing reveals the flaws in our thinking and shows how well (or bad) we understand things.</p>

<h2 id="give-it-a-try">Give it a try</h2>

<p>Some people say that writing is art. That may be true for the likes of Poe or Bradbury. My problem with calling it art by default is that it puts it on a pedestal. For me, writing is like carpentry. It’s a skill that must be honed every day. Something that takes time and effort, sure enough, but is entirely within reach of anyone.</p>

<p>So give it a try.</p>]]></content><author><name>Tommy Fernandez</name></author><category term="writing" /><summary type="html"><![CDATA[This is why I decided work as a writer.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://tomfern.com/images/writing.jpg" /><media:content medium="image" url="https://tomfern.com/images/writing.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">7 Minikube Tips</title><link href="https://tomfern.com/post/7-minikube-tips" rel="alternate" type="text/html" title="7 Minikube Tips" /><published>2020-06-28T18:04:00+01:00</published><updated>2020-06-28T18:04:00+01:00</updated><id>https://tomfern.com/post/Minikube-tips-06-30---7-minikube-tips</id><content type="html" xml:base="https://tomfern.com/post/7-minikube-tips"><![CDATA[<p>I love my Arch Linux, which I’ve been using it for years, more than I care to count. But sometimes I believe that all of us Linux users, deep in our heart of hearts, have some Windows- or Mac-only software that we covet.</p>

<p>For me, that software is Docker Desktop; not much for the sake of Docker, but mainly for the standalone Kubernetes cluster.</p>

<p>I’m afraid I can’t give you Docker Desktop for Linux, but I’ll offer the next best thing instead:</p>

<h2 id="7-tips-to-get-the-most-out-of-minikube">7 tips to get the most out of Minikube</h2>

<p><strong>Use the Dashboard</strong></p>

<p>This is one of the best features for me. Type <code class="language-plaintext highlighter-rouge">minikube dashboard</code>, which starts the Web UI that lets you manage all your Kubernetes resources. You can even start a terminal in a pod for a quick debug.</p>

<p><img src="/images/minikube-dashboard.png" alt="Dashboard" /></p>

<p><strong>Periodically Reset Minikube</strong></p>

<p>Every now and then, delete the cluster with <code class="language-plaintext highlighter-rouge">minikube delete</code> and start a new one with <code class="language-plaintext highlighter-rouge">minikube start</code>. I’ve been bitten by strange networking problems in the past and they all cleared up everytime after resetting the cluster.</p>

<p><strong>SSH to the VM</strong></p>

<p><a href="https://kubernetes.io/docs/setup/learning-environment/minikube/">Minikube</a> runs a single-node Kubernetes on your laptop. Sometimes we need to log in to the VM to get a closer look. The problem is that it runs in headless mode, so we don’t have a terminal window. But Minikube has a built-in ssh command for this: <code class="language-plaintext highlighter-rouge">minikube ssh</code></p>

<p><strong>File Sync</strong></p>

<p>If you put a file in <code class="language-plaintext highlighter-rouge">$HOME/.minikube/files</code> it will automatically be copied into the VM when you start the cluster.</p>

<p>For instance you can override the DNS servers in the VM like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir</span> <span class="nt">-p</span> ~/.minikube/files/etc
<span class="nv">$ </span><span class="nb">echo </span>nameserver 1.1.1.1 <span class="o">&gt;</span> ~/.minikube/files/etc/resolv.conf
<span class="nv">$ </span>minikube start
</code></pre></div></div>

<p><strong>Automount $HOME</strong></p>

<p>Did you know that if you start Minikube with <code class="language-plaintext highlighter-rouge">minikube start --mount</code> it automatically mounts <code class="language-plaintext highlighter-rouge">$HOME</code> in the VM? On my machine, home gets mapped to <code class="language-plaintext highlighter-rouge">/minikube-host</code> (which is different that <a href="https://minikube.sigs.k8s.io/docs/handbook/mount/">documented path</a>).</p>

<p>You can also mount any directory in the VM with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>minikube mount /path/on/host:/path/on/vm &amp;
</code></pre></div></div>

<p><strong>Try Different Versions</strong></p>

<p>It’s a good idea to always use the same Kubernetes version that you have in production. You can change the local version with <code class="language-plaintext highlighter-rouge">--kubernetes-version</code>. For example:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>minikube start <span class="nt">--kubernetes-version</span><span class="o">=</span><span class="s2">"v1.18.5"</span>
</code></pre></div></div>

<p><strong>Connect with Tunnel</strong></p>

<p>You may have noticed this when you deploy a load balancer service:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl get service
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP    PORT<span class="o">(</span>S<span class="o">)</span>        AGE
my-service   LoadBalancer   10.97.97.121   &lt;pending&gt;      80:32446/TCP   88s
</code></pre></div></div>

<p>It never gets assigned an external IP.</p>

<p>At first, I didn’t pay much attention to that, I just went with the links provided <code class="language-plaintext highlighter-rouge">minikube service --url SERVICE_NAME</code>.</p>

<p>But see what happens when you start a tunnel (do this on a second terminal)</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>minikube tunnel
</code></pre></div></div>

<p>Now suddenly the LoadBalancer service has an IP:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl get service
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP    PORT<span class="o">(</span>S<span class="o">)</span>        AGE
my-service   LoadBalancer   10.97.97.121   10.97.97.121   80:32446/TCP   88s
</code></pre></div></div>

<p>And it should be connectable:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl 10.97.97.121:80
</code></pre></div></div>

<!-- The service has a valid IP now, and that's not all, you can connect to it as long as the tunnel is running. -->
<!-- 
Why I want it when I can just port forward?
minikube tunnel
minikube tunnel --cleanup
kubectl port-forward POD PORT:PORT -->

<!-- - Addons, did you know minikube has addons? I didn't.
minikube addons list
minikube addons enable metrics-server -->

<hr />

<p>I’m glad we have Minikube; it’s an excellent way to try out and develop Kubernetes applications. I hope these tips helped you get the most out of it.</p>

<p>-Tomas 👋</p>]]></content><author><name>Tommy Fernandez</name></author><category term="kubernetes" /><summary type="html"><![CDATA[A mix of tips and less know features.]]></summary></entry><entry><title type="html">10 Vim Plugins for Writers</title><link href="https://tomfern.com/post/vim-for-writers" rel="alternate" type="text/html" title="10 Vim Plugins for Writers" /><published>2019-12-15T17:04:00+00:00</published><updated>2019-12-15T17:04:00+00:00</updated><id>https://tomfern.com/post/vim-for-writers</id><content type="html" xml:base="https://tomfern.com/post/vim-for-writers"><![CDATA[<p><img src="/images/writing.jpg" alt="" /></p>

<p>I still remember the first time I came in contact with Vim; it was during my very first job. It was a part-time gig in a government office. A few months in, they decided to go open-source full in. Out the window went Windows and in came Linux. At the time, I was doing PHP development, and I was sorry to have to let go of my dear Dreamweaver.</p>

<p>I decided that if I had to work on Linux, I would go all the way, no compromises for me. That meant getting used to working on the console and learning Vim.</p>

<p>When one of the resident Linux gurus walked by my seat and saw my screen, he said, “Are you using Vi? Maybe you’re not so useless after all.” (Funny story, I came across him on facebook the other day. Now that he’s bald, he doesn’t look intimidating at all).</p>

<p>Lately, I’ve been writing a lot. I found that armed with the right plugins, Vim is a great tool writing:</p>

<ul>
  <li><strong>vim-pencil</strong>: my favorite writing plugin. <a href="https://github.com/reedes/vim-pencil">Vim-pencil</a> brings a ton of nice things like navigation aids, smarter undo based on punctuation, and proper soft wrapping.</li>
  <li><strong>vim-ditto</strong>: <a href="https://github.com/dbmrq/vim-ditto">ditto</a> highlights repeated words in a paragraph, just what I need to avoid repeating words all the time.</li>
  <li><strong>vim-goyo</strong>: a Writeroom lookalike for Vim, <a href="https://github.com/junegunn/goyo.vim">goyo</a> removes all distracting elements like modeline and line numbers.</li>
  <li><strong>vim-colors-pencil</strong>: an elegant, low contrast <a href="https://github.com/reedes/vim-colors-pencil">colorscheme</a> geared towards writing.</li>
  <li><strong>vim-litecorrect</strong>: <a href="https://github.com/reedes/vim-litecorrect">litecorrect</a> automatically corrects common typing errors like “teh” instead of “the”.</li>
  <li><strong>vim-lexical</strong>: combined spellchecker and thesaurus. <a href="https://github.com/reedes/vim-lexical">Vim-lexical</a> lets me navigate between spell errors with <code class="language-plaintext highlighter-rouge">]s</code>, <code class="language-plaintext highlighter-rouge">[s</code> and quickly find synonyms with <code class="language-plaintext highlighter-rouge">&lt;leader&gt; t</code></li>
  <li><strong>vim-textobj-sentence</strong>: a <a href="https://github.com/reedes/vim-textobj-sentence">plugin</a> for better sentence navigation. I can move between sentences with <code class="language-plaintext highlighter-rouge">(</code> and <code class="language-plaintext highlighter-rouge">)</code>, I can cut a sentence with <code class="language-plaintext highlighter-rouge">dis</code>. Depends on <a href="https://github.com/kana/vim-textobj-user">vim-textobj-user</a>.</li>
  <li><strong>vim-textobj-quote</strong>: this <a href="https://github.com/reedes/vim-textobj-quote">plugin</a> smartly creates “quotes” so I don’t have to.</li>
  <li><strong>ALE</strong>: the <a href="https://github.com/dense-analysis/ale">Asynchronous Lint Engine</a> is a polyglot analysis tool that is not limited to code. It supports a bunch of style checkers like <a href="http://proselint.com/">proselint</a> and <a href="https://languagetool.org/">LanguageTool</a>.</li>
  <li><strong>vim-orgmode</strong>: I’ll admit that, before I saw the errors of my ways, I communed with <a href="https://https://www.gnu.org/software/emacs/">the one that should not be named</a>. During the time I used Emacs, I never fully switched away from Vim, that alone should have told me something.</li>
</ul>

<p>For my labors, I gained something invaluable: I found <a href="https://orgmode.org/">Org mode</a>. For me, Org-mode is the most intuitive and straightforward way of interacting with text. This filetype <a href="https://github.com/jceb/vim-orgmode">plugin</a> only implements a subset of all its features, but even so, it’s good enough for my purposes.</p>

<p>While not Vim-related, I also find these tools irreplaceable in my workflow:</p>

<ul>
  <li><a href="https://pandoc.org/">pandoc</a>: a command-line markup converter. Supports dozens of formats, including Org-mode and markdown. I usually write in Org and then export to the target type.</li>
  <li><a href="https://daringfireball.net/projects/titlecase/TitleCase.pl">TitleCase.pl</a>: John Gruber’s title case Perl script. I haven’t found a good native Title Case plugin for Vim, so I just use a <code class="language-plaintext highlighter-rouge">!TitleCase.pl</code> filter.</li>
  <li><a href="https://github.com/joeyespo/grip">grip</a>: view local Markdown files with GitHub renderer. I use it to preview how my files will look before pushing them.</li>
</ul>

<!--
- Grammarly: a life saver, I love it so much I subscribed to the premium plan. Even on the free tier, it’s so good to spot common mistakes other checkers will miss. It not perfect, but works well with markdown and org-mode content.
- [Hemingway App](http://www.hemingwayapp.com/): I’m trying this one out. It marks words and sentences that can be
-->

<!--
I don’t think I’m going to move away from Vim anymore. I tried most of the other editors: Visual Studio Code, Atom, Sublime Text, Eclipse, and lots more. They just don’t feel as satisfying to work with as Vim.
-->

<p>Happy writing!</p>

<p>Tomas</p>]]></content><author><name>Tommy Fernandez</name></author><category term="writing" /><category term="vim" /><summary type="html"><![CDATA[A text editor is so much more than mere software.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://tomfern.com/images//writing.jpg" /><media:content medium="image" url="https://tomfern.com/images//writing.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Case study: MariaDB ColumnStore</title><link href="https://tomfern.com/post/case-mariadb-columnstore" rel="alternate" type="text/html" title="Case study: MariaDB ColumnStore" /><published>2019-03-04T18:29:00+00:00</published><updated>2019-03-04T18:29:00+00:00</updated><id>https://tomfern.com/post/case-mariadb-columnstore</id><content type="html" xml:base="https://tomfern.com/post/case-mariadb-columnstore"><![CDATA[<p><img src="/images/database.jpg" alt="" /></p>

<p>A recent project I worked on provided an opportunity to work with MariaDB ColumnStore.</p>

<h2 id="the-product">The Product</h2>

<p>The product is a business intelligence application. Its main view, a very comprehensive dashboard with an abundance of counters,
showing all operational aspects: from server status, database backups, support tickets and SLA to datacenter atmospheric conditions.</p>

<p>In the middle of all this: several MariaDB databases fed through multiple ETL jobs.</p>

<p>As the application grew, the dashboard got increasingly more intricate. The tables got bigger and the queries slower.
To cope with the higher load, aggregate tables were added at some point. These tables contained precomputed subtotals for various predefined filters.</p>

<h3 id="the-issue">The Issue</h3>

<p>The aggregate tables worked really well for a very long time.
However, it still had its downsides, all of which got worse as data ingestion rate increased:</p>

<ul>
  <li>db schema and operation gets more complex</li>
  <li>tables need to be updated frequently as new data is added</li>
  <li>higher resource utilization, locks and timeouts during update jobs</li>
  <li>even on the best cases, some lag between precomputed and actual data is always present</li>
</ul>

<p>That last one was causing the big trouble. Dashboards where getting out of date fast, new data was being added faster than it could be processed.
As a result, users where more frequently seeing stale data.</p>

<h3 id="the-requirements">The Requirements</h3>

<p>The main requirement is to have the dashboards to be as close as possible to real time, while keeping page load times within reason.</p>

<p>Several solutions were proposed, including partitioning, sharding and adding more replication slaves.
After talking and discussing options, we settled trying out MariaDB ColumnStore.</p>

<p>Some reasons we thought it would be a good solution:</p>

<ul>
  <li>Familiarity: They were already running MySQL and MariaDB.</li>
  <li>Simplicity: We could get rid of the aggregate tables and their update processes.</li>
  <li>Integration: No need to overhaul the whole setup, we could copy the tables at our convenience.</li>
</ul>

<h2 id="the-columnstore">The ColumnStore</h2>

<p>MariaDB ColumnStore (CS) is a special version of MariaDB,
it supports all the traditional MariaDB features while providing an additional storage engine: ColumnStore, for columnar-based tables.</p>

<p>But while MariaDB has a 1 query = 1 CPU restriction, CS is all about parallelism.
Not only a single query can be processed by multiple CPUs: queries can be distributed among multiple CS servers,</p>

<h3 id="the-test">The Test</h3>

<p>For the test I was given a modest VM with 8 CPUs and 15 GB of RAM.
We picked a pilot table, extracted one month worth of data from the table (about 5M rows) and proceded to run the tests.</p>

<p>As a “control group”, a vanilla MariaDB server was installed and the usual production queries were benchmarked.</p>

<p>Then the second phase began: after removing MariaDB, a CS server was installed and data imported in a ColumnStore table.
Again, bechmarks were run.</p>

<p>Here are the times to compute an aggregate table:
<img src="/images/plots/case-mariadb-columnstore/query_load.png" alt="Computing the aggregate table" /></p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>InnoDB</th>
      <th>ColumnStore</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Query Time</td>
      <td>7.549 s</td>
      <td>0.169 s</td>
    </tr>
  </tbody>
</table>

<p>Quite impressive: about 44 times faster.</p>

<p>We can’t claim victory yet, as this does not really reflect the actual user experience.
Remember the app is fed from the precomputed aggregate table.</p>

<p>The real test is about estimating page load time. How does the realtime aggregation compares against getting the precomputed table?</p>

<p><img src="/images/plots/case-mariadb-columnstore/opmode2.png" alt="" /></p>

<table>
  <thead>
    <tr>
      <th>Query Time</th>
      <th>1 thread</th>
      <th>2 threads</th>
      <th>4 threads</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>InnoDB</td>
      <td>0.005 s</td>
      <td>0.006 s</td>
      <td>0.010 s</td>
    </tr>
    <tr>
      <td>ColumnStore</td>
      <td>0.169 s</td>
      <td>0.242 s</td>
      <td>0.443 s</td>
    </tr>
  </tbody>
</table>

<p>From the user perspective, performance with CS is <em>worse</em>, the page load time increases in about 2 orders of magnitude.
True, the data show is as realtime as possible. But can we do better?</p>

<p>Yes, of course. So far we only scratched the surface.</p>

<p>MariaDB ColumnStore has 3 modes of operation, these can be set at global level or session level:</p>

<ul>
  <li>Mode 0: generic row-by-row mode, highly compatible but much slower.</li>
  <li>Mode 1: distributed, very fast but non-parallelizable queries are rejected.</li>
  <li>Mode 2: automatic, switches between mode 0 and 1 depending on query.</li>
</ul>

<p>Mode 1 is the default, which means it wants to be fed good queries.
To make the previous tests work, I had to switch to mode 0, so we’re not
getting the best possible performance.</p>

<p>With a surprisingly little bit of rewriting, the same queries were running on mode 1.</p>

<p>Let’s see how well it does:
<img src="/images/plots/case-mariadb-columnstore/opmodes_vs_innodb.png" alt="Aggregate table vs ColumnStore" /></p>

<table>
  <thead>
    <tr>
      <th>Query Time</th>
      <th>1 thread</th>
      <th>2 threads</th>
      <th>4 threads</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>InnoDB (aggregate)</td>
      <td>0.005 s</td>
      <td>0.006 s</td>
      <td>0.010 s</td>
    </tr>
    <tr>
      <td>ColumnStore (mode 1)</td>
      <td>0.048 s</td>
      <td>0.068 s</td>
      <td>0.117 s</td>
    </tr>
    <tr>
      <td>ColumnStore (mode 0)</td>
      <td>0.169 s</td>
      <td>0.242 s</td>
      <td>0.443 s</td>
    </tr>
  </tbody>
</table>

<p>Query times improved a lot, realtime <em>mode 1</em> takes only 1 order of magnitude more than getting the precomputed data, that’s quite a feat.
Page load times are still worse, but it’s very much within the usual latency for a web application.</p>

<h2 id="the-solution">The Solution</h2>

<p>Once the plan was green-lighted, I proceeded to install a proper CS server (only one node for now, but we can add more later).</p>

<p>To provide load-balancing a MariaDB MaxScale proxy was added.
This proved a key component as it also allows to capture changes on the
main db, which are then sent to CS without needing to modify any other processes.</p>

<p>With proper load balancing in place, we took to opportunity to add more slave replicas to increase the performance of the general queries.</p>

<h3 id="caveats">Caveats</h3>

<p>Some problems we encountered along the way:</p>

<ul>
  <li>Not all queries were as easy to optimize for mode 1.</li>
  <li>Not all aggregation functions are available for CS tables:
Things like <code class="language-plaintext highlighter-rouge">SUM(FIND_IN_SET(code,'0,1'))</code> don’t work. In some cases, we had to add additional columns to work around this.</li>
</ul>

<!--listend-->

<ul>
  <li>Datatypes in CS are more strict than in MariaDB, JOINS require types to match much more closely.</li>
  <li>Some queries where trying to aggregate too many columns at a time, which probably made sense of a Row based database. But for CS we found
it’s better to split them in separate queries.</li>
  <li>Big joins can fail due to lack of memory, disk-based joins are disabled by default. Enabling the feature fixes the issue,
but performance goes down fast. As a result, we had to tweak joins and where conditions on some queries.</li>
</ul>

<h2 id="related-links">Related links</h2>

<ul>
  <li>MariaDB ColumnStore: <a href="https://mariadb.com/kb/en/library/mariadb-columnstore/">https://mariadb.com/kb/en/library/mariadb-columnstore/</a></li>
  <li>Operating modes: <a href="https://mariadb.com/kb/en/library/columnstore-operating-mode/">https://mariadb.com/kb/en/library/columnstore-operating-mode/</a></li>
  <li>Aggregate functions: <a href="https://mariadb.com/kb/en/library/columnstore-distributed-aggregate-functions/">https://mariadb.com/kb/en/library/columnstore-distributed-aggregate-functions/</a></li>
  <li>MariaDB MaxScale: <a href="https://mariadb.com/kb/en/maxscale/">https://mariadb.com/kb/en/maxscale/</a></li>
  <li>Change Data Capture: <a href="https://mariadb.com/kb/en/mariadb-maxscale-22-change-data-capture-cdc-protocol/">https://mariadb.com/kb/en/mariadb-maxscale-22-change-data-capture-cdc-protocol/</a></li>
</ul>

<h2 id="thanks">Thanks</h2>

<p>I’d like to thank the customer who very graciously allowed me to publish this entry and share the experience.</p>

<p>Later</p>

<p>Tomas</p>]]></content><author><name>Tommy Fernandez</name></author><category term="mariadb" /><summary type="html"><![CDATA[Taking advantage of column-based databases.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://tomfern.com/images/database.jpg" /><media:content medium="image" url="https://tomfern.com/images/database.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>