<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 <title>Work Posts · HookRace Blog</title>
 <link href="https://hookrace.net/blog/feed/work/" rel="self"/>
 <link href="https://hookrace.net/blog/work/"/>
 <updated>2026-02-10T21:22:29-05:00</updated>
 <id>https://hookrace.net/blog/Work</id>
 <author>
   <name>Dennis Felsing</name>
   <email>dennis@felsing.org</email>
 </author>

 
 <entry>
   <title>Workload Capture &amp; Replay</title>
   <link href="https://hookrace.net/blog/workload-capture-replay/"/>
   <updated>2026-02-10T00:00:00-05:00</updated>
   <id>https://hookrace.net/blog/workload-capture-and-replay</id>
   <content type="html">
     &lt;p&gt;When customers hit issues in production, it can be an effort to locally reproduce them, especially when external sources are involved. Reproducing issues is useful not just to figure out the root cause, but also to verify the fix and add a regression test. The newly introduced workload capture &amp;amp; replay tooling records a Materialize instance’s state as well as recent queries and ingestion rates, then replays them in a Docker Compose environment with synthetic data. In this blog post I’ll show how it works and talk about some of the challenges and future work.&lt;/p&gt;

&lt;p&gt;Read the rest of the blog post over on the &lt;a href=&quot;https://materialize.com/blog/materialize-workload-capture-replay/&quot;&gt;Materialize blog&lt;/a&gt;.&lt;/p&gt;

   </content>
 </entry>
 
 <entry>
   <title>Speeding up Materialize CI</title>
   <link href="https://hookrace.net/blog/speeding-up-materialize-ci/"/>
   <updated>2025-08-08T00:00:00-04:00</updated>
   <id>https://hookrace.net/blog/speeding-up-materialize-ci</id>
   <content type="html">
     &lt;p&gt;In the &lt;a href=&quot;https://materialize.com/blog/qa-process-overview/&quot;&gt;previous post&lt;/a&gt; I talked about how we test Materialize. This time I’ll describe how I significantly sped up our Continuous Integration (CI) Test pipeline in July, especially for pull requests that require a build and full test run. The goal is to make developers more productive by reducing the time waiting for CI to complete.&lt;/p&gt;

&lt;p&gt;We always kept CI runtime in mind, but it still slowly crept up over the years through adding tests, the code itself growing larger, as well as hundreds of minor cuts adding up.&lt;/p&gt;

&lt;p&gt;Read the rest of the blog post over on the &lt;a href=&quot;https://materialize.com/blog/speeding-up-materialize-ci/&quot;&gt;Materialize blog&lt;/a&gt;.&lt;/p&gt;

   </content>
 </entry>
 
 <entry>
   <title>How to Use the Materialize Emulator</title>
   <link href="https://hookrace.net/blog/materialize-emulator/"/>
   <updated>2024-10-16T00:00:00-04:00</updated>
   <id>https://hookrace.net/blog/materialize-emulator</id>
   <content type="html">
     &lt;p&gt;In our last blog about our Quality Assurance (QA) team, we gave an overview of the QA process, including our software and testing methods. One of our key tools during testing is the &lt;a href=&quot;https://materialize.com/download/#get-started&quot;&gt;Materialize Emulator&lt;/a&gt;, a Docker image that allows you to maintain a locally hosted version of Materialize.&lt;/p&gt;

&lt;p&gt;Read the rest of the blog post over on the &lt;a href=&quot;https://materialize.com/blog/materialize-emulator/&quot;&gt;Materialize blog&lt;/a&gt;.&lt;/p&gt;

   </content>
 </entry>
 
 <entry>
   <title>The CI Flake</title>
   <link href="https://hookrace.net/blog/the-ci-flake/"/>
   <updated>2024-08-16T00:00:00-04:00</updated>
   <id>https://hookrace.net/blog/the-ci-flake</id>
   <content type="html">
     &lt;p&gt;I analyzed a &lt;a href=&quot;https://buildkite.com/materialize/test/builds/88495#01915a27-cdd8-422b-b459-32de02308820&quot;&gt;flaky test failure&lt;/a&gt; in our &lt;a href=&quot;https://materialize.com/&quot;&gt;Materialize&lt;/a&gt; &lt;a href=&quot;https://buildkite.com/materialize&quot;&gt;CI&lt;/a&gt; today:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ docker compose up -d --scale default=0 default
no such service: default
mzcompose: error: running docker compose failed (exit status 1)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I had seen this error already once or twice in the last year, but it was incredibly rare in our Continuous Integration (CI) runs, and never happened locally. As usual, there were more pressing product issues to debug, so I never looked into it. But last week I switched most of our CI tests to run on &lt;a href=&quot;https://www.hetzner.com/cloud/&quot;&gt;Hetzner Cloud&lt;/a&gt; instead of &lt;a href=&quot;https://aws.amazon.com/&quot;&gt;AWS&lt;/a&gt; to save some money. Suddenly this issue started occurring more often in CI, so my thinking was that it must somehow be timing-dependent.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Before investigating my first instinct was that we are not writing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yaml&lt;/code&gt; file properly, which had already led to &lt;a href=&quot;https://github.com/MaterializeInc/materialize/issues/23481&quot;&gt;flaky calls to Docker Compose&lt;/a&gt; before (&lt;a href=&quot;https://github.com/MaterializeInc/materialize/blob/6809a54012c4d67a1088417662adea63c840c5a6/misc/python/materialize/mzcompose/composition.py#L326&quot;&gt;source code&lt;/a&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TemporaryFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set_inheritable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fileno&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So &lt;a href=&quot;https://github.com/MaterializeInc/materialize/pull/29041&quot;&gt;yesterday&lt;/a&gt; I added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file.flush()&lt;/code&gt; after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yaml.dump&lt;/code&gt; and hoped to be done with it. This morning I woke up and the issue was still occurring!&lt;/p&gt;

&lt;p&gt;Based on the logged &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; call this should be the code causing the problem (&lt;a href=&quot;https://github.com/MaterializeInc/materialize/blob/main/misc/python/materialize/cli/mzcompose.py#L591-L608&quot;&gt;source code&lt;/a&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handle_composition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argparse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;composition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Composition&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflow&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;composition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Restart any dependencies whose definitions have changed.
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# This is Docker Compose&apos;s default behavior for `up`, but
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# not for `run`, which is a constant irritation that we
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# paper over here. The trick, taken from Buildkite&apos;s
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# Docker Compose plugin, is to run an `up` command that
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# requests zero instances of the requested service.
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;composition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;--scale&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflow&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;=0&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;handle_composition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;composition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Running the test in an endless loop locally had no success of reproducing the issue:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
    &lt;/span&gt;bin/mzcompose &lt;span class=&quot;nt&quot;&gt;--find&lt;/span&gt; skip-version-upgrade down
    bin/mzcompose &lt;span class=&quot;nt&quot;&gt;--find&lt;/span&gt; skip-version-upgrade run default &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;break
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I modified the conditional in the above Python code to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if True:&lt;/code&gt; and surely that reproduces the issue:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ bin/mzcompose --find skip-version-upgrade run default
$ docker compose up -d --scale default=0 default
no such service: default
mzcompose: error: running docker compose failed (exit status 1)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So how is it possible to be landing in this code path sometimes? I checked how the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;composition.workflows&lt;/code&gt; are generated and it looks quite complex indeed (&lt;a href=&quot;https://github.com/MaterializeInc/materialize/blob/6809a54012c4d67a1088417662adea63c840c5a6/misc/python/materialize/mzcompose/composition.py#L136-L152&quot;&gt;source code&lt;/a&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;mzcompose_py&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mzcompose.py&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mzcompose_py&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;importlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;spec_from_file_location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mzcompose&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mzcompose_py&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;importlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;module_from_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;importlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;composition_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exec_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;composition_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getdoc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getmembers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isfunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;startswith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;workflow_&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# The name of the workflow is the name of the function
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# with the &quot;workflow_&quot; prefix stripped and any underscores
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# replaced with dashes.
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;workflow_&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Since we are reading and analyzing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mzcompose.py&lt;/code&gt; file, something might be running in parallel and corrupting/overwriting it. If I empty the file I can indeed reproduce the issue with the original code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ echo &amp;gt; test/skip-version-upgrade/mzcompose.py
$ bin/mzcompose --find skip-version-upgrade run default
$ docker compose up -d --scale default=0 default
no such service: default
mzcompose: error: running docker compose failed (exit status 1)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It feels like I’m getting closer, but nothing should be running in parallel, even in CI each agent is running in isolation, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt; is long finished at this point based on the CI logs, so it should not interfere. As an additional frustration, I’ve been wondering why this flake is mostly (but not only) occurring when running this test? The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mzcompose.py&lt;/code&gt; file in question is one of the simplest ones around.&lt;/p&gt;

&lt;p&gt;Until here it felt like I was getting closer, but now I had no more leads. I went as far as searching for Python bugs around inheritance that could explain this issue, but if Python was buggy in such basic behavior, this code definitely wouldn’t be the first one to run into it. Could it be a cosmic ray?!&lt;/p&gt;

&lt;p&gt;Since this issue seemed unexplainable without the universe conspiring against me, I went back to the basics and checked what else is running in CI and I found something extremely suspicious just before the actual test execution in our CI script (&lt;a href=&quot;https://github.com/MaterializeInc/materialize/blob/6809a54012c4d67a1088417662adea63c840c5a6/ci/plugins/mzcompose/hooks/command#L63-L69&quot;&gt;source code&lt;/a&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# Start dependencies under a different heading so that the main&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# heading is less noisy. But not if the service is actually a&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# workflow, in which case it will do its own dependency management.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; mzcompose &lt;span class=&quot;nt&quot;&gt;--mz-quiet&lt;/span&gt; list-workflows | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$service&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;ci_collapsed_heading &lt;span class=&quot;s2&quot;&gt;&quot;:docker: Starting dependencies&quot;&lt;/span&gt;
    mzcompose up &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--scale&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$service&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=0&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$service&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Oh no! There is another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$service=0&lt;/code&gt; call here, my entire investigation so far might have been useless! From looking at the CI output it’s not clear which one is actually running, but the fact that I’ve never seen the flake locally points to the new suspect. After all this code is only running in CI, and not locally, all in the name of cleaner CI logs! Checking the CI output there is a strange line occurring:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;write /dev/stdout: broken pipe&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Well, there is a pipe between the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mzcompose&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; processes, but why would it break? Let’s run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mzcompose&lt;/code&gt; command in isolation:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ bin/mzcompose --find skip-version-upgrade list-workflows
default
test-version-skips&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It lists all workflows, of which we have two. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; command is only looking for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; line. Apparently, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; is smart enough to exit immediately when it finds a match when used with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-q&lt;/code&gt; (&lt;a href=&quot;https://man7.org/linux/man-pages/man1/grep.1.html&quot;&gt;manpage&lt;/a&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;-q, --quiet, --silent
       Quiet; do not write anything to standard output. Exit
       immediately with zero status if any match is found, even if
       an error was detected. Also see the -s or --no-messages option.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Easy enough to verify, I ran the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; command, which exits instantly:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ bin/mzcompose --find skip-version-upgrade list-workflows | true
Exception ignored in: &amp;lt;_io.TextIOWrapper name=&apos;&amp;lt;stdout&amp;gt;&apos; mode=&apos;w&apos; encoding=&apos;utf-8&apos;&amp;gt;
BrokenPipeError: [Errno 32] Broken pipe&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;broken pipe&lt;/code&gt; output is indeed explained by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; exitting early. But why does the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mzcompose&lt;/code&gt; process breaking lead to the code in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt;’s body being executed? Normally the first command in a pipe failing would be ignored, since the second command succeeded, and I’m seeing the return code being &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; here. Checking the beginning of the script reveals that we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pipefail&lt;/code&gt; setting:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-euo&lt;/span&gt; pipefail&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Bash’s &lt;a href=&quot;https://www.man7.org/linux/man-pages/man1/bash.1.html&quot;&gt;manpage&lt;/a&gt; explains:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The return status of a pipeline is the exit status of the last command, unless the pipefail option is enabled.  If pipefail is enabled, the pipeline’s return status is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Typically, Python buffers its output when it doesn’t write directly to a terminal, so I still can’t reproduce the issue with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; locally. Then I remembered that we had an issue with our Python tests’ stdout and stderr output being garbled, which &lt;a href=&quot;https://github.com/MaterializeInc/materialize/pull/17846&quot;&gt;made us enable&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PYTHONUNBUFFERED&lt;/code&gt; in our ci-builder &lt;a href=&quot;https://github.com/MaterializeInc/materialize/blob/078ebd39d3dec03383be74eca862d4820116de59/ci/builder/Dockerfile&quot;&gt;Dockerfile&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-docker&quot; data-lang=&quot;docker&quot;&gt;&lt;span class=&quot;c&quot;&gt;# Ensure that all python output is unbuffered, otherwise it is not&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# logged properly in Buildkite&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; PYTHONUNBUFFERED=1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With this revelation the flake was easy enough to reproduce. Just running this would fail occasionally, still less than 1% on my system:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ set pipefail
$ PYTHONUNBUFFERED=1 bin/mzcompose --find skip-version-upgrade list-workflows | grep -q &quot;default&quot;
BrokenPipeError: [Errno 32] Broken pipe&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Adding a small &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sleep&lt;/code&gt; between each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print&lt;/code&gt; leads to a reliable failure:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;gh&quot;&gt;diff --git a/misc/python/materialize/cli/mzcompose.py b/misc/python/materialize/cli/mzcompose.py
index 3a2f5dbb81..725150ed00 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/misc/python/materialize/cli/mzcompose.py
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/misc/python/materialize/cli/mzcompose.py
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -319,6 +319,7 @@&lt;/span&gt; class ListWorkflowsCommand(Command):
         composition = load_composition(args)
         for name in sorted(composition.workflows):
             print(name)
&lt;span class=&quot;gi&quot;&gt;+            time.sleep(0.1)
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;

&lt;/span&gt; class DescribeCommand(Command):&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PYTHONUNBUFFERED&lt;/code&gt; it would have taken us 8 KiB of output before we’d run into the broken pipe, none of our tests has that many workflows.&lt;/p&gt;

&lt;p&gt;That concludes this long investigation into what turned out to be an interesting flake that we’ve been seeing in CI! The &lt;a href=&quot;https://github.com/MaterializeInc/materialize/pull/29070&quot;&gt;trivial fix&lt;/a&gt; is removing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-q&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; call, but we could also have disabled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pipefail&lt;/code&gt; for that one pipe or we could have unset &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PYTHONUNBUFFERED&lt;/code&gt;.
One lesson from this debugging session is that even simple behaviors can combine in unexpected ways to create strange flaky failures. In this case all of these conspired to make this CI flake possible:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Misdirection: Run different code in CI compared to locally&lt;/li&gt;
  &lt;li&gt;Switch to another kind of server with slightly different performance characteristics&lt;/li&gt;
  &lt;li&gt;Disable Python’s output buffering&lt;/li&gt;
  &lt;li&gt;Enable Bash’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pipefail&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; run quietly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In hindsight I realized why this specific &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skip-version-upgrade/mzcompose.py&lt;/code&gt; was having the flake most often. It is one of the smallest test files:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ wc -l test/*/mzcompose.py | sort -n
      39 test/pg-rtr/mzcompose.py
      43 test/mysql-rtr/mzcompose.py
      67 test/debezium/mzcompose.py
[...]
      85 test/skip-version-upgrade/mzcompose.py
[...]
     966 test/0dt/mzcompose.py
    1333 test/bounded-memory/mzcompose.py
    1916 test/limits/mzcompose.py
    4767 test/cluster/mzcompose.py&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But other than the test definitions that are even smaller and never had this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;no such service: default&lt;/code&gt; error, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skip-version-upgrade/mzcompose.py&lt;/code&gt; file contains multiple workflows, so the smaller files could not run into the issue at all:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ grep &quot;def workflow_&quot; test/*/mzcompose.py | cut -d&quot;:&quot; -f1 | \
  uniq -c | sort -n | grep -v &quot;   1 &quot;
   2 test/skip-version-upgrade/mzcompose.py
   3 test/canary-environment/mzcompose.py
[...]
  57 test/cluster/mzcompose.py&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;


   </content>
 </entry>
 
 <entry>
   <title>Testing Materialize</title>
   <link href="https://hookrace.net/blog/testing-materialize/"/>
   <updated>2024-05-14T00:00:00-04:00</updated>
   <id>https://hookrace.net/blog/testing-materialize</id>
   <content type="html">
     &lt;p&gt;I joined Materialize’s Quality Assurance (QA) development team over a year ago. Since our team is small, we have to be conscious about focusing our time on the most impactful testing and test tooling.&lt;/p&gt;

&lt;p&gt;Our goal is to find issues in Materialize as early and as efficiently as possible, ideally before a pull request even lands. Despite our small team, Materialize has a huge surface area.&lt;/p&gt;

&lt;p&gt;Read the rest of the blog post over on the &lt;a href=&quot;https://materialize.com/blog/qa-process-overview/&quot;&gt;Materialize blog&lt;/a&gt;.&lt;/p&gt;

   </content>
 </entry>
 
 <entry>
   <title>Database Testing at Yugabyte</title>
   <link href="https://hookrace.net/blog/database-testing-at-yugabyte/"/>
   <updated>2023-01-14T00:00:00-05:00</updated>
   <id>https://hookrace.net/blog/database-testing-at-yugabyte</id>
   <content type="html">
     &lt;p&gt;&lt;a href=&quot;https://www.yugabyte.com/yugabytedb/&quot;&gt;YugabyteDB&lt;/a&gt; is a cloud-native database for business-critical enterprise applications. It is designed to provide continuous availability as well as horizontal scalability, while retaining a strong set of RDBMS features. This objective creates a strong quality incentive for us in the Yugabyte Quality Assurance (QA) team. As a member of this team, I am giving an overview of the testing philosophy, approaches, and implementations for YugabyteDB.&lt;/p&gt;

&lt;p&gt;Read the rest of the blog post over on the &lt;a href=&quot;https://www.yugabyte.com/blog/yugabytedb-database-testing/&quot;&gt;YugabyteDB blog&lt;/a&gt;.&lt;/p&gt;

   </content>
 </entry>
 
 <entry>
   <title>€9 Ticket</title>
   <link href="https://hookrace.net/blog/euro9-ticket/"/>
   <updated>2022-08-20T00:00:00-04:00</updated>
   <id>https://hookrace.net/blog/euro9-ticket</id>
   <content type="html">
     &lt;p&gt;As a relief for rising energy costs and inflation Germany is offering nearly-free public transportation for the months of June, July and August. For just €9 per month you can take any kind of regional public transportation in all of Germany. That’s basically everything except the IC/ICE for long distance. This means that a monthly ticket is now as cheap as what I would normally pay for a single route. In this post I want to talk about my experiences with this &lt;a href=&quot;https://www.bahn.com/en/offers/regional/9-euro-ticket-en&quot;&gt;€9 ticket&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Update 2022-09-01: The €9 ticket ended yesterday without a similar replacement system in place. So many people are again back to paying about €100 per month for much smaller regional tickets instead (see for example this &lt;a href=&quot;https://old.reddit.com/r/de/comments/x2x01s/70_euro_f%C3%BCr_4_stationen_endlich_zur%C3%BCck_in_meinem/&quot;&gt;German Reddit thread&lt;/a&gt;. Financially it makes more sense for me personally to go back to driving. (I hope the gasoline in the car is still good after three months.) My hope is that these three months will stay in people’s minds in Germany and shape the future of public transportation to be cheaper and simpler:&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/fahrkartenautomat.webp&quot;&gt;&lt;img src=&quot;/public/9euro/fahrkartenautomat.webp&quot; alt=&quot;Fahrkartenautomat&quot; /&gt;&lt;/a&gt;
Image via &lt;a href=&quot;https://old.reddit.com/r/de/comments/26rty0/ausland_vs_deutschland/&quot;&gt;Reddit&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;train-stations&quot;&gt;Train Stations&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://assets.static-bahn.de/dam/jcr:f55563b4-0ccf-4669-bb92-459a5988691c/20220221_LN_S-Bahn_RN_867x385_Mireo.pdf&quot;&gt;&lt;img style=&quot;width: 100vw; max-width: 100vw; margin-left: -50vw; position: relative; left: 50%&quot; alt=&quot;VRN Plan&quot; src=&quot;/public/9euro/vrn.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above graphic you can see the train stations in our area. Since we are situated in the &lt;a href=&quot;https://en.wikipedia.org/wiki/Rhine-Neckar&quot;&gt;Rhine-Neckar metropolitan region&lt;/a&gt; the railway network is pretty good here.&lt;/p&gt;

&lt;iframe class=&quot;osm-map&quot; allowfullscreen=&quot;&quot; src=&quot;//umap.openstreetmap.fr/en/map/train-stations_798502?scaleControl=true&amp;amp;miniMap=false&amp;amp;scrollWheelZoom=false&amp;amp;zoomControl=true&amp;amp;allowEdit=false&amp;amp;moreControl=false&amp;amp;searchControl=null&amp;amp;tilelayersControl=null&amp;amp;embedControl=null&amp;amp;datalayersControl=true&amp;amp;onLoadPanel=undefined&amp;amp;captionBar=false&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;(map made with &lt;a href=&quot;https://umap.openstreetmap.fr/en/&quot;&gt;umap&lt;/a&gt;, routes made with &lt;a href=&quot;https://www.graphhopper.com/&quot;&gt;GraphHopper&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The closest train stop from us is St. Ilgen-Sandhausen at 2 km distance. It offers direct connections to the cities of Heidelberg (160k inhabitants), Mannheim (309k), Karlsruhe (313k), Darmstadt (159k) and Frankfurt (753k). For other directions I prefer going to a different train station since switching times are either too long or there is too much risk of missing the connecting train. Only 89% of regional trains are &lt;a href=&quot;https://www.deutschebahn.com/de/konzern/konzernprofil/zahlen_fakten/puenktlichkeitswerte-6878476&quot;&gt;on time&lt;/a&gt; in Germany, with some routes and stations having much worse rates. This is a far cry from Switzerland’s &lt;a href=&quot;https://www.admin.ch/gov/de/start/dokumentation/medienmitteilungen.msg-id-83485.html&quot;&gt;95% punctuality&lt;/a&gt; with an even stricter definition of 3 minutes versus 5 minutes delay in Germany.&lt;/p&gt;

&lt;p&gt;Another closeby train station we commonly use is HD-Weststadt/Südstadt at 6 km distance, which is good to cycle and can also be reached via Tram. Occasionally I used Heidelberg Hauptbahnhof as well as Wiesloch-Walldorf, both at slightly under 10 km distance, which are a bit larger and thus are used as stops for Regional-Express trains. These go faster than the Regionalbahn and S-Bahn trains and thus also have fewer stops.&lt;/p&gt;

&lt;h2 id=&quot;cycling-trips&quot;&gt;Cycling Trips&lt;/h2&gt;

&lt;iframe class=&quot;osm-map&quot; allowfullscreen=&quot;&quot; src=&quot;//umap.openstreetmap.fr/en/map/cycling-and-jogging-routes_798422?scaleControl=true&amp;amp;miniMap=false&amp;amp;scrollWheelZoom=false&amp;amp;zoomControl=true&amp;amp;allowEdit=false&amp;amp;moreControl=false&amp;amp;searchControl=null&amp;amp;tilelayersControl=null&amp;amp;embedControl=null&amp;amp;datalayersControl=true&amp;amp;onLoadPanel=undefined&amp;amp;captionBar=false&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;The above map contains some of the cycling trips I took by combining them with the €9 ticket. (map made with &lt;a href=&quot;https://umap.openstreetmap.fr/en/&quot;&gt;umap&lt;/a&gt;, routes made with &lt;a href=&quot;https://www.graphhopper.com/&quot;&gt;GraphHopper&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Last time I posted about cycling and work was about &lt;a href=&quot;/blog/cycling-to-work/&quot;&gt;commuting by bicycle for a year&lt;/a&gt;. I kept this up and was super happy with this way of getting to &lt;a href=&quot;https://www.sap.com/&quot;&gt;SAP&lt;/a&gt;’s headquarter in Walldorf and the included exercise. After a while I moved together with my wife and my bike commute lengthened to 10 km each direction, which was still perfectly fine. Then Covid forced me in March of 2020 to work from home I switched to regular cycling trips in the afternoon since I started working early in the morning, in order to have meetings with colleagues in Korea and China. Two 50 km cycling trips per week got me to the same level as the daily commute used to do, and I have a nice route I regularly take, which covers a lot of forest and lake.&lt;/p&gt;

&lt;p&gt;In January I switched to a new job at &lt;a href=&quot;https://www.yugabyte.com/&quot;&gt;Yugabyte&lt;/a&gt;. As the database we develop is distributed, so are the people I work with. Since most meetings fall into US times, I am now working more during the afternoon and evening. This means my cycling trips moved to the morning instead.&lt;/p&gt;

&lt;p&gt;Previously I had a job ticket for three years, but other than the €9 ticket it limits the range I can take public transportation in, so that many of my destinations would be barely outside of the covered range. When looking at a map of Germany with all the different transport associations, each with their own tickets, prices and rules, I feel reminded of &lt;a href=&quot;https://de.m.wikipedia.org/wiki/Datei:HRR_1789.png&quot;&gt;historical maps of the Holy Roman Empire&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/verbuende.png&quot;&gt;&lt;img src=&quot;/public/9euro/verbuende.png&quot; alt=&quot;Public Transport Associations in Germany&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good luck figuring out what ticket to get to combine with your job ticket. Luckily the €9 ticket changed this, at least temporarily, making life simpler.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/oldtown.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/oldtown.jpg&quot; alt=&quot;Old town at Neckar&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used the ticket during the working days to discover new cycling routes. By cycling one direction and taking a direct train connection back, I can cover nearly twice the distance as usual with the same starting and stopping point. With this method I took many routes I had never taken before, discovering beautiful cycling paths along the Neckar river for example. Luckily the trains are not too crowded during working days, especially outside of rush hour. The cycling usually takes around three times as long as the train ride back.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/empty.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/empty_small.jpg&quot; alt=&quot;Empty train&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image above shows an almost empty train during a working day.&lt;/p&gt;

&lt;p&gt;Even with a single train connection things can go wrong though. One time the train in front of ours started burning and so we got delayed for an unknown time. But since I had my bicycle with me, that meant I could just cycle back the rest of the way and get some extra exercise for free. If I was really out of energy I could have waited in the train, but often busses are used as a replacement, and bicycles on busses don’t fit well.&lt;/p&gt;

&lt;p&gt;The weather was unusually dry the last months, the last few days had the first few drops of rain I remember since the beginning of June. This is of course bad for nature here, as I saw at the Rhine river, which is much lower than usual. For cycling it’s quite good though, only got wet once.&lt;/p&gt;

&lt;h2 id=&quot;jogging&quot;&gt;Jogging&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/forest.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/forest.jpg&quot; alt=&quot;&amp;quot;Kleiner Odenwald&amp;quot; forest&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This amount of cycling of course meant that my bicycle had to get some repairs done. While I had it at the repair shop, I tried out jogging from Heidelberg back to my home town Leimen. The route goes through the “Kleiner Odenwald” forest for nearly the entire way, and the rest are some fields. It goes through Heidelberg’s highest mountain, the Königstuhl, with a nice view of the city and surrounding area.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/koenigstuhl.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/koenigstuhl_small.jpg&quot; alt=&quot;View from Königstuhl&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The “Ladder to Heaven” in the beginning with its 1300 steps accounts for most of the elevation and is still too tough for me to do at faster than walking speed. After I got my bicycle back I still kept doing this jogging route a few times per week.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/9euro/jogging.png&quot; alt=&quot;Elevation for Jogging&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;nearby-excursions&quot;&gt;Nearby Excursions&lt;/h2&gt;

&lt;p&gt;There are lots of cities and nice nature nearby. So we used public transportation for excursions on every weekend. It was noticable that the trains were crowded, and at times people couldn’t even enter the train anymore. When going to the cinema we noticed that we should really catch the last train at 22:30, since the next connection would take all night.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/excursion.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/excursion_small.jpg&quot; alt=&quot;Cinema excursion&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One time the train back from Frankfurt was late, then got cancelled. The next train was also delayed, and now overcrowded because it had twice the passengers. Unfortunately it took me too long to realize that with more than 20 minutes delay, we should be able to take high speed ICE trains as a replacement and get the cost reimbursed by Deutsche Bahn.&lt;/p&gt;

&lt;p&gt;You still need to wear masks as a Covid prevention on public transportation in Germany. This is basically the only remaining occasion, other than at the Doctor’s office, where you have to wear masks. Even with all the travel using public transportation and basically no distancing we haven’t caught Covid as far as we are aware based on symptoms and some rapid tests.&lt;/p&gt;

&lt;h2 id=&quot;eating-out&quot;&gt;Eating Out&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/food1.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/food1_small.jpg&quot; alt=&quot;Food&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My wife picked out many great restaurants in nearby cities for us to try out. By car it would not make sense to just drive to another city for a dinner, but using public transportation we had no problem spending an hour on a train, where you can comfortable talk or surf the web, to reach our destinations. This probably has a really good economic effect on restaurants and cafes, many were so fully packed that we got the last seats or had to reserve beforehand.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/food2.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/food2_small.jpg&quot; alt=&quot;More food&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;family-visits&quot;&gt;Family Visits&lt;/h2&gt;

&lt;p&gt;For trips to cities and cycling trips we mostly take direct train connections. That usually works quite well. To visit family however the destinations are smaller villages, so the connections are worse and there is no direct connection at all. So a 40 minute drive turns into a 2 hour train ride with 3 different trains instead. Coincidentally this is the same time I need for the route by bicycle.&lt;/p&gt;

&lt;p&gt;There is a relatively high chance of missing a connecting train, or of getting stuck in the middle of the route. One time a truck crashed into the train bridge, so our train couldn’t continue. Instead of waiting around for an hour or two we took the train back home and, for the only time during these three months so far, had to switch to the car to keep our timeline. At least the highway was quite empty, there seems to be some conflicting data on whether the €9 ticket actually has an effect on reducing car traffic though.&lt;/p&gt;

&lt;h2 id=&quot;getting-visits&quot;&gt;Getting Visits&lt;/h2&gt;

&lt;p&gt;While my wife and I didn’t do any long distance travel using the €9 ticket, others did. I had a co-developer of &lt;a href=&quot;https://ddnet.org/&quot;&gt;DDraceNetwork&lt;/a&gt; visit all the way from Berlin using the €9 ticket. For people who have time it has been much easier to visit others with the €9 ticket, without having to worry about cost.&lt;/p&gt;

&lt;h2 id=&quot;while-on-vacation&quot;&gt;While on Vacation&lt;/h2&gt;

&lt;p&gt;While we didn’t use the €9 ticket to go to a vacation, it was still useful during a car-based vacation. While driving to Berchtesgadener Land we saw a train line that seemed to lead to Berchtesgaden. So we stopped at the closeby train station and spontaneously took the train instead, which lead us through beautiful sceneries.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/berchtesgaden.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/berchtesgaden.jpg&quot; alt=&quot;Berchtesgaden&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the way back from the same vacation we had a stop in Munich. Instead of driving the car all the way into the city center I checked for the closest free Park and Ride garage from the highway. After parking the car there, the train was unfortunately cancelled, so we had to take a bus and metro, but still reached the city center quickly.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/foreign1.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/foreign1_small.jpg&quot; alt=&quot;Public transportation on Mallorca&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After having used public transportation a lot back at home, we also felt more comfortable using it on vacation in other countries, even if it costs a bit more there. So on Mallorca we explored the entire island using the extensive bus network instead of renting a car. The photo above shows the most distant bus stop we reached, at a lighthouse at the end of a long curvy road. Next week we are taking the TGV to Paris, which tops out at 320 km/h and manages the 550 km route in just 2:36, an average of 212 km/h. The image below is luckily not the TGV:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/9euro/foreign2.jpg&quot;&gt;&lt;img src=&quot;/public/9euro/foreign2_small.jpg&quot; alt=&quot;More public transportation on Mallorca&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Going to the airport by train worked fine, but we had to get there a few hours earlier in case was some train delay causing us to miss the connecting train. On the way back there indeed was a huge delay, with a train line being closed down for hours. After a slow trickle of information from the train operator, we had to figure out an alternative route on our own, since the DB app was still showing routes that could not be operated for the next few hours because of the train line closure.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;There are still two weeks left for the €9 ticket and I’m planning to make the best use of them. So far I’ve been taking a trip on most days, so it’s definitely been worth it and will be missed.&lt;/p&gt;

&lt;p&gt;Unfortunately the €9 ticket won’t be prolonged. The high public interest has lead to &lt;a href=&quot;https://www.thelocal.de/20220805/how-the-greens-want-to-replace-germanys-e9-ticket-deal/&quot;&gt;some plans&lt;/a&gt; for a relatively cheap follow-up ticket, but again with some regional limitations for the cheap ticket. &lt;a href=&quot;https://www.theguardian.com/money/2022/jul/15/spain-announces-free-rail-journeys-from-september-until-the-end-of-the-year&quot;&gt;Other&lt;/a&gt; &lt;a href=&quot;https://www.vdl.lu/en/getting-around/bus/free-public-transport-and-exceptions&quot;&gt;countries&lt;/a&gt; are even implementing entirely free public transport meanwhile, so there is some hope for the future.&lt;/p&gt;

   </content>
 </entry>
 
 <entry>
   <title>One year of cycling to work</title>
   <link href="https://hookrace.net/blog/cycling-to-work/"/>
   <updated>2018-02-20T00:00:00-05:00</updated>
   <id>https://hookrace.net/blog/cycling-to-work</id>
   <content type="html">
     &lt;p&gt;Exactly on this day, one year ago, I came back from a one month long trip to
Taiwan, went straight to work from the airport and immediately moved into a new
apartment after work. Since then I have cycled to work nearly every day.&lt;/p&gt;

&lt;!--more--&gt;

&lt;iframe allowfullscreen=&quot;&quot; class=&quot;osm-map&quot; src=&quot;https://umap.openstreetmap.fr/en/map/one-year-of-cycling-to-work_199573?scaleControl=true&amp;amp;miniMap=false&amp;amp;scrollWheelZoom=true&amp;amp;zoomControl=false&amp;amp;allowEdit=false&amp;amp;moreControl=false&amp;amp;searchControl=false&amp;amp;tilelayersControl=false&amp;amp;embedControl=false&amp;amp;datalayersControl=false&amp;amp;onLoadPanel=undefined&amp;amp;captionBar=false&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;a href=&quot;https://umap.openstreetmap.fr/en/map/one-year-of-cycling-to-work_199573#13/49.3166/8.6605&quot;&gt;Fullscreen&lt;/a&gt; (map made with &lt;a href=&quot;https://umap.openstreetmap.fr/en/&quot;&gt;umap&lt;/a&gt;, routes made with &lt;a href=&quot;https://www.graphhopper.com/&quot;&gt;GraphHopper&lt;/a&gt;)&lt;/p&gt;

&lt;h2 id=&quot;alternatives-and-motivation&quot;&gt;Alternatives and motivation&lt;/h2&gt;

&lt;p&gt;Previously I lived in Heidelberg, 15 km from work and usually took the train,
only cycling to work about once a week. But my new apartment was significantly
closer to work, only 6 km, which takes just 15-20 minutes to cycle, so it
became much easier to use the bicycle to get to work every day.&lt;/p&gt;

&lt;p&gt;Taking public transportation instead is possible, and takes about 35-40
minutes. I only do that when I want to go to the city straight after work
instead of going home first. For me personally public transportation is more
useful in combination with the bicycle on longer trips.&lt;/p&gt;

&lt;p&gt;Sometimes I take public transportation to work and just walk back in the
evening, which can be great for listening to audiobooks.&lt;/p&gt;

&lt;p&gt;For about a month I had borrowed a car and sometimes used it to go to work.
Without traffic the driving would take 12 minutes, but with traffic it’s
usually 15-25 minutes.  Additionally I have to get a parking spot and walk
further to the office, so using a car doesn’t save time compared to cycling.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20170414_091754.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20170414_091754_small.jpg&quot; alt=&quot;Public transportation&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;advantages&quot;&gt;Advantages&lt;/h2&gt;

&lt;p&gt;I remember feeling tired when getting to work early in the morning using public
transportation. After all you just keep sitting and standing, never really jump
starting the body. While cycling to work I certainly wake up and feel more
energetic and focussed when I arrive.&lt;/p&gt;

&lt;p&gt;Having a clear separation of work and private life is important for me, both by
using different spaces for each as well as having a strict time separation. So
after I get out of work and get onto my bicycle I will still be having
work-related thoughts circling in my head. But while cycling I might take a
quick stop to write down any insights that I have or tasks that I forgot to
finish. Then I can finally shut down and clear my brain from work by enjoying
the ride, the views and the physical exercise.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20170409_184910-PANO.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20170409_184910-PANO_small.jpg&quot; alt=&quot;Stream&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I find the simplicity of cycling for locomotion beautiful. I never have to be
stuck in a traffic jam, feeling stressed by moving at a snail’s pace. Even if
cycling is slower the feeling of making progress is stronger. I never have to
be waiting for a late train, a bus that left too early or be stuck inside of a
train while there are problems with the power line. In the worst case I can
pick up my bicycle and walk around some obstacle or fix a flat tire in a few
minutes. Being outside in nature, at least for half an hour every day, feels
good.&lt;/p&gt;

&lt;p&gt;Combined with the gym at work I feel like I’m getting enough sports to be
reasonably fit without having to spend much time on it. I regularly get home
from work at 17:00 with all work and sports done and can enjoy the rest of the
day without any worry.&lt;/p&gt;

&lt;p&gt;When I’m using the bicycle to get to work already, I feel like the barrier to
combining this with larger trips is lowered. I occasionally do 40-50 km cycling
tours before or after work, depending on the weather and my mood.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20170614_172048.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20170614_172048_small.jpg&quot; alt=&quot;Longer trip&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;accommodation&quot;&gt;Accommodation&lt;/h2&gt;

&lt;p&gt;At home my bicycle is locked into a room in the basement, making it reasonably
secure and still easily accessible. At work we have roofed-over parking spaces
for the bicycles with robust metal bars for locking. Some of the cycle racks
even have two floors with an easy mechanism for getting your bicycle down from
there. When I don’t need my lock I will just leave it there after work for the
next day.&lt;/p&gt;

&lt;p&gt;Multiple buildings at work have special showers for cyclists. But to be honest
I don’t use them after my usual route to work, for three reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It is cooler in the morning.&lt;/li&gt;
  &lt;li&gt;The distance is short enough.&lt;/li&gt;
  &lt;li&gt;I cycle slower when going to work in the morning than when going back.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Especially in the summer it is nice to do a two hour cycling trip at 5 am and
go straight to work afterwards. Then the showers are a godsend of course.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20170404_073617.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20170404_073617_small.jpg&quot; alt=&quot;Morning fog&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-journey-is-the-reward&quot;&gt;The journey is the reward&lt;/h2&gt;

&lt;p&gt;Close to my home I have to cross a red light, which is also where I had the
only accident in this year. A car driver ignored my right of way and so I had
to break hard and ended up sitting on their hood. But luckily that didn’t
result in any injuries or damages. (Edit: I have never had an accident
involving a car before, so this is probably an outlier.)&lt;/p&gt;

&lt;p&gt;After exiting the industrial area I am riding exclusively on small field roads.
Especially in the morning this makes for nice sunrises with fog on parts of the
fields. Along the way there is also a fishing lake.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20171018_170639.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20171018_170639_small.jpg&quot; alt=&quot;Fishing lake&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The way tunnels below train tracks, runs over a stream, tunnels below a large
street and finally bridges over another large street. This means there are no
large intersections where you have to wait or be cautious. In consequence the
ride is quite relaxing.&lt;/p&gt;

&lt;p&gt;The only potential stop along the way is a small airfield where planes are
occasionally landing and starting. But that’s fun to watch, so I don’t mind
waiting for a minute or two.&lt;/p&gt;

&lt;div class=&quot;startvideo&quot;&gt;&lt;div class=&quot;video-container&quot;&gt;
  &lt;div class=&quot;ytplayer&quot; data-id=&quot;lx6VtG5S-AI&quot;&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;script src=&quot;/public/youtube.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;numbers&quot;&gt;Numbers&lt;/h2&gt;

&lt;p&gt;There are about 250 work days in a year in Baden-Württemberg, excluding
weekends and public holidays. Of those I was missing for 34 days for vacations,
being sick and doing home office. On 11 days I used public transportation, on 5
days a car. That leaves 200 days on which I cycled to work.&lt;/p&gt;

&lt;p&gt;The distance to work is 6 km each way, so in total I cycled 2400 km just to get
to work and back. I need 20 minutes to work and 15 minutes back, so 117 hours
were spent cycling.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/T-Randonneur_Apex_2x10.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/T-Randonneur_Apex_2x10_small.jpg&quot; alt=&quot;T-Randonneur (mine is in green though)&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had two flat tires, one accident and needed one major part replacement in the
entire year. But to be fair I also cycled in my free time and it was the first
large replacement my bicycle needed since I started using it 15.000 km ago.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20170928_183309.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20170928_183309_small.jpg&quot; alt=&quot;New parts&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;animal-nature&quot;&gt;Animal nature&lt;/h2&gt;

&lt;p&gt;On one day in summer I counted the wild animals that I saw on my short trip to
work, which included 4 rabbits, 1 deer with fawn, 1 white stork with young in
the nest as well as a group of 21 white storks in the fields. A few weeks ago I
was happy to see an elusive black stork flying towards the closeby forest for
the first time. On another occurence there was a Heron successfully catching a
snake.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20170602_162849.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20170602_162849_small.jpg&quot; alt=&quot;Storks&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apart from wild animals there are of course many people walking their dogs, and
the pet home on the way features lots of dogs behind the fence as well as a few
cats walking around outside.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20171016_162141.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20171016_162141_small.jpg&quot; alt=&quot;Lazy cats&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;winter-is-coming&quot;&gt;Winter is coming&lt;/h2&gt;

&lt;p&gt;Appropriate clothing is important since cycling exposes you to wind and
precipitation.&lt;/p&gt;

&lt;p&gt;In summer a t-shirt and short pants are usually fine. For colder times
dual-function pants for office and cycling are great, otherwise jeans also
work.&lt;/p&gt;

&lt;p&gt;During rains I certainly don’t want to wear jeans. Then I also need to bring
another set of clothes to switch to once I’m in the office. The most important
measure for rainy days is to have good timing, since I have flexible work times
and the rains are usually not equally strong for hours. Weather forecasts can
be remarkably inaccurate though.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20160118_144437.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20160118_144437_small.jpg&quot; alt=&quot;Cycling in snow (photo from 2016)&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use wind-proof jackets and shoes as well as gloves and a balaclava in winter.
Snow and ice are rather rare and not so strong here, so they are not a big
issue. The above photo was taken two years ago and that represents about the
worst winter conditions I ever had to cycle in.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/public/cycling/IMG_20170526_183012.jpg&quot;&gt;&lt;img src=&quot;/public/cycling/IMG_20170526_183012_small.jpg&quot; alt=&quot;The road ahead&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ll keep cycling to work in the foreseeable future. I don’t think this post is
of much use to anyone, but I had fun reflecting my last year of cycling to
work. Thanks for reading. Comments on &lt;a href=&quot;https://news.ycombinator.com/item?id=16420271&quot;&gt;Hacker News&lt;/a&gt;.&lt;/p&gt;

   </content>
 </entry>
 
 <entry>
   <title>Turning Down a Blockchain Job Offer</title>
   <link href="https://hookrace.net/blog/turning-down-blockchain/"/>
   <updated>2018-01-22T00:00:00-05:00</updated>
   <id>https://hookrace.net/blog/turning-down-blockchain</id>
   <content type="html">
     &lt;p&gt;I have recently received a job offer to work on a blockchain implementation. While the offer was very generous, I had to turn it down. In this post I want to collect the thoughts that went into my decision process leading to this conclusion.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;I have not touched blockchain technology much before, other than reading the original &lt;a href=&quot;https://bitcoin.org/bitcoin.pdf&quot;&gt;Bitcoin paper&lt;/a&gt; shortly after it was released and accepting Bitcoin donations for &lt;a href=&quot;https://ddnet.org/&quot;&gt;DDNet&lt;/a&gt;. But I have always used those donations pretty much immediately, and thus I have no investment in cryptocurrencies either way and am thus impartial to their fate.&lt;/p&gt;

&lt;h2 id=&quot;the-good&quot;&gt;The Good&lt;/h2&gt;

&lt;p&gt;I consider the Bitcoin idea to be quite interesting as an electronic cash system that doesn’t require any trusted intermediary. This is a small and simple idea that combines old cryptographic ideas into something new.&lt;/p&gt;

&lt;p&gt;The job I got offered had a significantly higher salary attached than my current job. The new job would have been remote, thus enabling me to spend more time with my girlfriend and live in any location I wish.&lt;/p&gt;

&lt;p&gt;I would have been able to work in a nice programming language, developing a blockchain implementation from the ground up with a small group of colleagues.&lt;/p&gt;

&lt;h2 id=&quot;the-bad&quot;&gt;The Bad&lt;/h2&gt;

&lt;p&gt;Proof of work in the form of mining is currently the most common form of trustless consensus in blockchain systems. It requires immense amounts of computational hardware in the form of ASICs and GPUs as well as energy to supply them. The only function of this is to prevent any one actor to take over more than 50% of the mining market and thus controlling the currency. This is a large price to pay for replacing trust.&lt;/p&gt;

&lt;p&gt;Looking at it the other way: Trust can be seen as a shortcut that humanity has used for a long time in order not to require expensive proof of work. For most applications this still works fine.&lt;/p&gt;

&lt;p&gt;Companies are doing initial coin offerings (ICOs), giving out their own cryptotoken to public investors, circumventing a proper IPO process. While companies often claim that their tokens will have another purpose, this is usually not true.&lt;/p&gt;

&lt;p&gt;Reading about blockchain is horrible because of all the hype, cargo cult and politics surrounding it. Everyone who is talking positively about cryptocurrencies and making hyperbolic claims seems to have invested in them. So they might be trying to pump up the value of cryptocurrencies in order to profit themselves. It might be that this aspect is so dominant that the actual technological details are less relevant. It feels like talking to a religious group that is trying to convert you to the one true faith that will solve all problems you and society have. I wish it was possible to filter out everyone who had money invested in the fate of cryptocurrencies in those discussions.&lt;/p&gt;

&lt;p&gt;It seems like people already know that they want to use a blockchain even before they understand what the problem is, basically a solution in search of problems. The main use of cryptocurrencies right now seems to be as a speculation device.&lt;/p&gt;

&lt;h2 id=&quot;the-conclusion&quot;&gt;The Conclusion&lt;/h2&gt;

&lt;p&gt;In the end I guess I don’t want to be associated with the current state of the blockchain ecosystem. So even if the job offer is great, I have to deny it. It took me a long time to realize this for myself and getting my priorities straight.&lt;/p&gt;

&lt;p&gt;Discussion on &lt;a href=&quot;https://news.ycombinator.com/item?id=16205776&quot;&gt;Hacker News&lt;/a&gt;.&lt;/p&gt;

   </content>
 </entry>
 
 <entry>
   <title>HANA C++ Development Environment and Processes</title>
   <link href="https://hookrace.net/blog/hana-cpp-development/"/>
   <updated>2017-12-08T00:00:00-05:00</updated>
   <id>https://hookrace.net/blog/hana-cpp-development</id>
   <content type="html">
     &lt;p&gt;I started working as a C++ developer in the HANA Core Platform team at SAP in Walldorf, Germany more than a year ago. In this time I have gotten some insights into the development environment and processes. I will use this post to illustrate them by the example of adding a small new feature and explaining the steps on the way of getting it released. Some of this will be specific to HANA, some to my team inside HANA, some to my own system.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;&lt;img src=&quot;/public/office.jpg&quot; alt=&quot;Office&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;compilation&quot;&gt;Compilation&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.sap.com/products/hana.html&quot;&gt;HANA&lt;/a&gt; is the in-house database powering many of SAP’s products. It is written in C++ with Python tests, and the entire code base lives inside of a single git repository. Hundreds of developers from all around the world are developing on about 10 million lines of C++ code and 15 million lines of Python tests.&lt;/p&gt;

&lt;p&gt;Since HANA is deployed on Linux exclusively, many developers are using Linux on their workstations as well. So far Windows is still supported as a development environment, but this will change in 2019, leaving Linux as the only choice. During day-to-day work you still get to interact with Windows a bit (more than I would prefer), since the Laptop has Windows with the traditional Microsoft Office products on it. But since the actual development happens on Linux, I am happy enough, being able to use the xmonad window manager and software environment I have gotten used to over the last decade.&lt;/p&gt;

&lt;p&gt;HANA is deployed on some &lt;a href=&quot;https://www.sap.com/dmc/exp/2014-09-02-hana-hardware/enEN/appliances.html#categories=certified&amp;amp;order=MemoryDesc&quot;&gt;rather impressive machines&lt;/a&gt;, so in order to test the code adequately the developer workstations are quite beefy as well. I am typing this text from a 20 core / 40 thread Xeon E5-2660 v3 CPU with 128 GB of RAM.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/htop.png&quot; alt=&quot;htop on developer machine&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Still compiling HANA is no quick feat. Expect something around 2 hours to build it from scratch on your local machine (illustrated below), and be prepared for some heat output, which is especially noticeable in summer. About half of the colleagues have chosen to move their workstations to the data center and access it remotely instead of working on it locally. Since they are still in one of our SAP buildings in Walldorf, the latency is low enough that a direct X connection is fast enough to be nearly undistinguishable from a local application.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/full-build.png&quot; alt=&quot;Illustration of full HANA build on developer machine&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So if even your beefy developer machine is not enough, how do you compile the product faster? Obviously by combining all of the developer machines together in a compile cluster:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/compile-cluster.png&quot; alt=&quot;Compile Cluster Overview&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Using the compile cluster the build time gets reduced to 16 minutes and now linking takes half of that time. Right now we are still using the gold linker, but after bug fixes lld should cut down the linking time significantly.&lt;/p&gt;

&lt;h2 id=&quot;coding&quot;&gt;Coding&lt;/h2&gt;

&lt;p&gt;But for the code in my team we have another approach. Instead of running the full database and testing using Python tests we have extensive unit tests, literally covering our entire code. Every single line is covered and about 98% of all regions. In order to submit any new code you have to write a unit test that will cover this exact code and include it in the same change.&lt;/p&gt;

&lt;p&gt;Expect 5 minutes to build and run the unit tests from scratch, while building and running them after a change can be as fast as 15 seconds if you didn’t change much and thus not much has to be recompiled.&lt;/p&gt;

&lt;p&gt;People use quite a variety of IDEs and code editors, with Sublime Text, Qt Creator, Eclipse, Visual Studio, emacs and vim being popular choices (in no particular order). Personally I like to work directly on terminals, so I use vim with the &lt;a href=&quot;https://github.com/Valloric/YouCompleteMe&quot;&gt;YouCompleteMe&lt;/a&gt; semantic code completion (and go to definition, type analysis and some more) based on clang/llvm. I use a &lt;a href=&quot;https://github.com/def-/ycmd/commit/2a7124bcb5730f301e3e73a4af81316bbff81926&quot;&gt;custom YCM version&lt;/a&gt; which also shows me the size and alignment of a variable along with its type:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;plangen::PipelineBuilder &amp;amp; =&amp;gt; hex::plangen::PipelineBuilder &amp;amp; (size: 128 B, align: 16 B)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using vim and other command line tools gives me the advantage that I can work just fine as long as I have an SSH connection to my workstation, nothing else required.&lt;/p&gt;

&lt;p&gt;Let’s start by coming up with a useful new feature, tracing the name of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DummyNoRun&lt;/code&gt; operator:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#pragma once
&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;hex/planex/framework/OperatorBases.hpp&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;hex/planex/planviz/HasReversedConnection.hpp&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;warnings/clang_warnings_hard.h&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;warnings/gcc_warnings_veryhard.h&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operators&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DummyNoRunOp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;final&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;planviz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasReversedConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;planex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoRunOperator&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceOperatorName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ltt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ostream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// namespace operators&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// namespace hex&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;warnings/clang_warnings_end.h&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;warnings/gcc_warnings_end.h&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The implementation is quite simple as well:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DummyNoRunOp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceOperatorName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ltt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ostream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;DummyNoRunOp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For HANA development we use C++14 at this moment. The only unusual thing is that we don’t use the STL, but instead have our own implementation of it, the LTT, which enforces allocator use.&lt;/p&gt;

&lt;p&gt;Our build system wraps around cmake, so it is quite reasonable to use. After adding the cpp file to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMakeLists.txt&lt;/code&gt; we can locally run our unit tests and verify that nothing broke so far.&lt;/p&gt;

&lt;p&gt;But when we’re compiling the code with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hm b -b Optimized unitHexOperators&lt;/code&gt; (read as: HappyMake build &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Optimized&lt;/code&gt; build [with optimizations and assertions] of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unitHexOperators&lt;/code&gt; target) we get an unexpected result:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;In file included from ../../hex/plangen/PredInitOpGenerator.cpp:14:0:
../../hex/operators/table/DummyNoRunOp.hpp:16:10: error: ‘virtual void hex::operators::DummyNoRunOp::traceOperatorName(ltt::ostream&amp;amp;) const’ can be marked override [-Werror=suggest-override]
     void traceOperatorName(ltt::ostream&amp;amp; os) const;
          ^~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors
ERROR: subcommand failed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our warning levels are quite high and all warnings are converted to errors. We even enable warnings in headers (and disable them again at the end in order to not affect other headers that might be included later). In this case the fix is quite simple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceOperatorName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ltt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ostream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now our code builds and the unit tests succeed:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ hm b -b Optimized runtests_hex
...
[==========] 728 tests from 147 test cases ran. (11617 ms total)
[  PASSED  ] 728 tests.

  YOU HAVE 2 DISABLED TESTS



Build started:	2017-11-29 16:26:40.459482
Build finished:	2017-11-29 16:26:52.988541
Elapsed time:	12.529s
Command count:	11 (0.9 per sec)
Log directory:	/home/dXXXXXX/src2/build/Optimized/hm_log/build/24
SUCCESS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can also run the code coverage based on our unit tests locally using CheckBot. Unsurprisingly we will find out that our code is not covered yet:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;###############################################################
                  CheckBot - Review Details
###############################################################

###############################################################
                      CheckBot - Summary
###############################################################

______________
General Issues
______________
[CheckHexCodeCoverageClang] (error) hex/operators/table/DummyNoRunOp.cpp: 3 of 3 lines uncovered, 0.00% coverage!
[CheckHexCodeCoverageClang] (error) Line coverage only at 99.99%
[CheckHexCodeCoverageClang] (info) Coverage report can be found at 
file://///home/dXXXXXX/src2/build/linuxx86_64-clangcov-release_hex_with_code_coverage/hexCoverageClang/index.html

CheckBot detected 3 issues (2 errors, 1 info).

	error ... CheckHexCodeCoverageClang  (681748.81 msec)

Overall score: -2

(Legend: ok = +1, info = +1, warning = -1, error = -2)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the bottom of the report we see the “Overall score: -2”, which indicates that our change would not be allowed to be merged by Gerrit. So let’s take a closer look, inside of the linked report we find our code:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/llvm-cov.png&quot; alt=&quot;llvm-cov html report&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Google Test/Mock based unit tests are used to test every part of the code in our team. 100% unit test line coverage is achieved with a development version of llvm-cov. Compared to the gcc based gcov/lcov coverage tools, the LLVM based llvm-cov is in active development, runs much faster and can show you what region of a line is actually covered, allowing finer granularity.&lt;/p&gt;

&lt;p&gt;So let’s write a unit test to satisfy the 100% line coverage requirement:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;hex/operators/table/DummyNoRunOp.hpp&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;hex/test/HexTestBase.hpp&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;hex/planex/test/sandbox/SimpleOperatorSandbox.hpp&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;warnings/clang_warnings_hard.h&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;warnings/gcc_warnings_hard.h&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;warnings/msvc_warnings_hard.h&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operators&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UnitDummyNoRunOp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HexTestBase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;protected:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;planex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SimpleOperatorSandbox&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DummyNoRunOp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()};&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ltt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ostringstream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_oss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;TEST_F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnitDummyNoRunOp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceOperatorName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;m_oss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;planex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TraceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m_sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;EXPECT_THAT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m_oss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StrEq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UnitDummyNoRunOp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// namespace test&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// namespace operators&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// namespace hex&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For more coarse-grained testing there are also JSON based component tests as well Python based integration tests available.&lt;/p&gt;

&lt;h2 id=&quot;local-verification&quot;&gt;Local Verification&lt;/h2&gt;

&lt;p&gt;We should also make use of some static code checkers, like clang-format and clang-tidy, before we push the change for remote testing. After all, this ensures a consistent style, fixes some common problems and reduces the mental load on the manual reviewers, since they can rely on the tools instead of complaining about the same trivial nitpick on every change:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;###############################################################
                  CheckBot - Review Details
###############################################################

_____________________________________________
hex/operators/table/test/UnitDummyNoRunOp.cpp
_____________________________________________

[CheckClangFormat] (info) This file is subject to a clang-format code style. CheckBot can reformat the file, see [...]

line 2,0 - 2,35:
#include &quot;hex/test/HexTestBase.hpp&quot;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[CheckClangFormat] (warning) 

 #include &quot;hex/test/HexTestBase.hpp&quot;

should be deleted

line 3,0 - 3,0:
#include &quot;hex/planex/test/sandbox/SimpleOperatorSandbox.hpp&quot;
^
[CheckClangFormat] (warning) 

 #include &quot;hex/test/HexTestBase.hpp&quot;

should be added
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Luckily clang-format can even fix the problem on its own by just adding a parameter.&lt;/p&gt;

&lt;h2 id=&quot;remote-verification&quot;&gt;Remote Verification&lt;/h2&gt;

&lt;p&gt;Finally we can push the change to Gerrit. Now our local work is done and we can start working on something else. Meanwhile automated builds and tests will happen on Linux and Windows machines, as well as a coverage run and various static code analyzers (that we were too lazy to run locally).&lt;/p&gt;

&lt;p&gt;By default just our unit tests and component tests are compiled and executed. But if you want to build the full HANA database and run the Python tests a separate profile is available for that and can be turned on in the Gerrit web interface for specific changes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/gerrit.png&quot; alt=&quot;Gerrit Overview after successful builds&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;code-review&quot;&gt;Code Review&lt;/h2&gt;

&lt;p&gt;Once we have verified that everything works fine on Linux with GCC and Clang as well as Windows with MSVC, we can add a reviewer to our change and set the change to “Ready for Review”. A second reviewer will be chosen automatically. If you are relatively sure that your change will survive the tests and don’t feel like waiting for them, you can of course also add the reviewers straight after submitting the change. But your reviewers might not be happy if they start reviewing already and meanwhile you are pushing new patch sets to fix compilation, tests as well as automatic warnings and suggestions.&lt;/p&gt;

&lt;p&gt;For non-trivial changes further patch sets would follow to integrate the suggestions by the reviewers, until everyone is (reasonably) happy. Finally the change can be submitted and is out of our mind.&lt;/p&gt;

&lt;h2 id=&quot;merging&quot;&gt;Merging&lt;/h2&gt;

&lt;p&gt;Except that there is still one thing missing. Our change is now inside of our component’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex&lt;/code&gt; branch, but not yet in the global &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orange&lt;/code&gt; branch. The merge from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orange&lt;/code&gt; will run a huge number of correctness and performance tests before allowing our changes in. That’s why we only run it after we have collected a few changes. We also use a staging branch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex2orange&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Initially we merge the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex&lt;/code&gt; branch into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex2orange&lt;/code&gt; and try to merge &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex2orange&lt;/code&gt; into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orange&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Meanwhile everyone can keep developing new features as well as bug fixes on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex&lt;/code&gt; branch.&lt;/li&gt;
  &lt;li&gt;If the merge from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex2orange&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orange&lt;/code&gt; fails, a fix for the issue will be submitted to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex&lt;/code&gt; and can finally be cherry-picked to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex2orange&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But we don’t merge &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex&lt;/code&gt; into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex2orange&lt;/code&gt; again, until we landed in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orange&lt;/code&gt; branch. Otherwise the new features from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex&lt;/code&gt; could cause new test failures while we were fixing the old ones. In the worst case we would never reach &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orange&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once the colleagues give a green light on all tests, the merge goes in and we can sit and wait for bugs for our new feature to roll in.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I hope you found something interesting in this post. On a related note, SAP HANA is &lt;a href=&quot;https://jobs.sap.com/search/?q=SAPhanacareers&amp;amp;locationsearch=&amp;amp;utm_source=DennisFelsing&quot;&gt;hiring right now in a few locations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Discuss on &lt;a href=&quot;https://news.ycombinator.com/item?id=15890028&quot;&gt;Hacker News&lt;/a&gt; and &lt;a href=&quot;https://www.reddit.com/r/programming/comments/7if7fa/hana_c_development_environment_and_processes/&quot;&gt;r/programming&lt;/a&gt;.&lt;/p&gt;

   </content>
 </entry>
 
 <entry>
   <title>C++ Quiz #1</title>
   <link href="https://hookrace.net/blog/cpp-quiz-1/"/>
   <updated>2017-12-01T00:00:00-05:00</updated>
   <id>https://hookrace.net/blog/cpp-quiz-1</id>
   <content type="html">
     &lt;p&gt;What is the output of this small snippet that is based on real C++ code?&lt;/p&gt;

&lt;!--more--&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;standard&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;copy&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;int&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;int, int&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Foo, int&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;int, Foo&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bar&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The code compiles without warnings with clang++ 5.0.0 as well as g++ 7.2.0 using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-Wall -Wextra&lt;/code&gt;.&lt;/p&gt;

&lt;script language=&quot;javascript&quot;&gt;
function toggle() {
    var spoiler = document.getElementById(&quot;spoiler&quot;);
    spoiler.style.display = (spoiler.style.display == &quot;block&quot;) ? &quot;none&quot; : &quot;block&quot;;
}
&lt;/script&gt;

&lt;h2&gt;&lt;a href=&quot;javascript:toggle();&quot;&gt;Solution&lt;/a&gt;&lt;/h2&gt;

&lt;div id=&quot;spoiler&quot; style=&quot;display: none&quot;&gt;
  &lt;p&gt;The preliminaries are simple:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;We have a struct Foo with a few different constructors, each of which prints a text that represents its parameter types.&lt;/li&gt;
    &lt;li&gt;We have a struct Bar with two members, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_i&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_j&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; function calls the standard constructor of Bar.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;The constructor of Bar is where the magic happens, so let’s go through it line by line:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f(Foo(m_i, m_j))&lt;/code&gt; creates a temporary Foo object by calling the Foo constructor with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_i&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_j&lt;/code&gt; as parameters, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int, int&lt;/code&gt; is printed. The resulting Foo object is then passed to the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f(Foo(m_i))&lt;/code&gt; works analogously, it calls  the Foo constructor with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_i&lt;/code&gt; as parameter, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; is printed. The resulting Foo object is then passed to the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo(m_i, m_j)&lt;/code&gt; works the same as the first line, except it doesn’t pass the resulting temporary Foo object to a function, so it is destroyed again immediately, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int, int&lt;/code&gt; is printed again.&lt;/li&gt;
    &lt;li&gt;So far so good, but now look closely. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo(m_i)&lt;/code&gt; surprisingly behaves entirely differently from all the previous lines. It does &lt;em&gt;not&lt;/em&gt; call the Foo constructor with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_i&lt;/code&gt; as the parameter. Instead it creates a Foo object of the name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_i&lt;/code&gt;, just like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo m_i&lt;/code&gt; would. So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;standard&lt;/code&gt; is printed.&lt;/li&gt;
    &lt;li&gt;Now the last line looks just like the third line, but it still does something different. Why? Because the name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_i&lt;/code&gt; is no longer referring to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int m_i&lt;/code&gt; member of Bar. Instead &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_i&lt;/code&gt; is now referring to a local variable of type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo&lt;/code&gt;, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo(m_i, m_j)&lt;/code&gt; prints &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Foo, int&lt;/code&gt;.&lt;/li&gt;
  &lt;/ol&gt;

  &lt;h2 id=&quot;explanation&quot;&gt;Explanation&lt;/h2&gt;

  &lt;p&gt;Thus spoke the C++ standard:&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;§8.3 Meaning of declarators&lt;/p&gt;

    &lt;p&gt;[…]&lt;/p&gt;

    &lt;p&gt;6 In a declaration &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T D&lt;/code&gt; where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;D&lt;/code&gt; has the form&lt;/p&gt;

    &lt;p&gt;&lt;em&gt;( D1 )&lt;/em&gt;&lt;/p&gt;

    &lt;p&gt;the type of the contained &lt;em&gt;declarator-id&lt;/em&gt; is the same as that of the contained &lt;em&gt;declarator-id&lt;/em&gt; in the declaration&lt;/p&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T D1&lt;/code&gt;&lt;/p&gt;

    &lt;p&gt;Parentheses do not alter the type of the embedded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;declarator-id&lt;/code&gt;, but they can alter the binding of complex declarators.&lt;/p&gt;
  &lt;/blockquote&gt;

  &lt;p&gt;This kind of ambiguous parsing can lead to dangerous situations, as is documented in &lt;a href=&quot;https://wiki.sei.cmu.edu/confluence/display/cplusplus/DCL53-CPP.+Do+not+write+syntactically+ambiguous+declarations&quot;&gt;DCL53-CPP&lt;/a&gt;:&lt;/p&gt;

  &lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;mutex&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shared_resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;increment_by_42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unique_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;shared_resource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

  &lt;p&gt;The code looks like it locks the mutex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m&lt;/code&gt; while modifiying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shared_resource&lt;/code&gt;, but instead a new mutex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m&lt;/code&gt; is created, shadowing the global mutex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m&lt;/code&gt;.&lt;/p&gt;

  &lt;h2 id=&quot;future-directions&quot;&gt;Future Directions&lt;/h2&gt;

  &lt;p&gt;The upcoming Clang version will have a warning for this:&lt;/p&gt;

  &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;y.cpp:25:12: warning: parentheses were disambiguated as redundant parentheses around declaration of variable named
      &apos;m_i&apos; [-Wvexing-parse]
        Foo(m_i);
           ^~~~~
y.cpp:25:9: note: add enclosing parentheses to perform a function-style cast
        Foo(m_i);
        ^
        (       )
y.cpp:25:12: note: remove parentheses to silence this warning
        Foo(m_i);
           ^   ~
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/div&gt;

   </content>
 </entry>
 
</feed>
