<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts on Blog Updates</title>
    <link>https://tekao.net/posts/</link>
    <description>Recent content in Posts on Blog Updates</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 12 Apr 2022 21:08:42 +1000</lastBuildDate><atom:link href="https://tekao.net/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Working With Opaque Data in Go</title>
      <link>https://tekao.net/posts/opaque_data_in_go/</link>
      <pubDate>Tue, 12 Apr 2022 21:08:42 +1000</pubDate>
      
      <guid>https://tekao.net/posts/opaque_data_in_go/</guid>
      <description>
        
          &lt;p&gt;Opaque data, is data where you don&amp;rsquo;t know what information it contains. For this article we are going to assume it has some structure: the data is in key, value form. The keys are strings. The values can be maps, arrays or single values.&lt;/p&gt;
&lt;p&gt;In Go this can be modeled as &lt;code&gt;map[string]interface{}&lt;/code&gt;. Most encoding packages will encode and decode into values using this type. To be more precise the two types used are &lt;code&gt;map[string]interface{}&lt;/code&gt; for maps and &lt;code&gt;[]interface{}&lt;/code&gt; for arrays. Using these limited types to model opaque data is a good idea, even if you are not encoding/decoding, rather than using &lt;code&gt;struct&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The meaning of the keys and values are not defined. But we still want to be able to work with data like this.&lt;/p&gt;
&lt;h3 id=&#34;merge-example&#34;&gt;Merge Example:&lt;/h3&gt;
&lt;p&gt;This function will merge any data in &lt;code&gt;src&lt;/code&gt; into &lt;code&gt;dst&lt;/code&gt; preserving the structure, but will not overwrite plain values. All that is required is to use a &lt;a href=&#34;https://go.dev/ref/spec#Switch_statements&#34;&gt;type switch&lt;/a&gt; to deal with maps and array structure. Having this limited set of map and array types to deal with makes this easy.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Go&#34; data-lang=&#34;Go&#34;&gt;&lt;span class=&#34;ln&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// doesn&amp;#39;t overwrite
&lt;/span&gt;&lt;span class=&#34;ln&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mergeMapStringInterface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 3&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 4&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 5&lt;/span&gt;      &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 6&lt;/span&gt;      &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}:&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 7&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{});&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 8&lt;/span&gt;          &lt;span class=&#34;nf&#34;&gt;mergeMapStringInterface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 9&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;10&lt;/span&gt;      &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}:&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;11&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].([]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{});&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;12&lt;/span&gt;          &lt;span class=&#34;nx&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;13&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;14&lt;/span&gt;      &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;15&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// don&amp;#39;t overwrite plain values
&lt;/span&gt;&lt;span class=&#34;ln&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;17&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;18&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;19&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;20&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;zero-values&#34;&gt;Zero Values&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s worth remembering that in Go indexing a map with a key which does not exist evalutes to the zero value for the type defined in the map. In other languages doing this can raise an exception.&lt;/p&gt;
&lt;p&gt;Also when using a &lt;a href=&#34;https://go.dev/ref/spec#Type_assertions&#34;&gt;type assertions&lt;/a&gt; that assign to two variables (the special form, with boolean check), if the type assertions fails it also results in the assignment to be the zero value of the asserted type.&lt;/p&gt;
&lt;p&gt;Combining these two features can make working with data easier&lt;/p&gt;
&lt;h3 id=&#34;checking-a-value&#34;&gt;Checking a Value&lt;/h3&gt;
&lt;p&gt;In the following code if the value for &amp;ldquo;expires&amp;rdquo; doesn&amp;rsquo;t exist or if it&amp;rsquo;s not of type &lt;code&gt;time.Time&lt;/code&gt; then we don&amp;rsquo;t run the expiry check. When using this method, what the zero value represents needs to be ruled out as being signficant, in many cases a zero value time.Time or empty string is invalid. (Note the brackets around the if clause, needed because of literal here, the parser needs this, a bit of 👀❓with Go, oldschool)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Go&#34; data-lang=&#34;Go&#34;&gt;&lt;span class=&#34;ln&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;expires&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fields&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;expires&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;expires&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;2&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Before&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;expires&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;3&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;loggedIn&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;4&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;the-meaning-of-things&#34;&gt;The Meaning of Things&lt;/h3&gt;
&lt;p&gt;Here we creating meaning out of &amp;ldquo;public_metrics.like_count&amp;rdquo; and &amp;ldquo;likeCount&amp;rdquo;. So we don&amp;rsquo;t know the data, it could be from some API, it&amp;rsquo;s opaque. But we&amp;rsquo;ve created meaning here, they are likes 👍📈. The way of thinking about this is treating data as opaque even though you may know roughly what the data is. Probably the best use of this, is giving the user of your app the power to give meaning to data they are interested in.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Go&#34; data-lang=&#34;Go&#34;&gt;&lt;span class=&#34;ln&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;likeSemantics&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;likes&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;public_metrics.like_count&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;likeCount&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 2&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getLikes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 4&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 5&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 6&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}:&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 7&lt;/span&gt;        &lt;span class=&#34;nf&#34;&gt;getLikes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 8&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}:&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt; 9&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v2&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;10&lt;/span&gt;          &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{});&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;11&lt;/span&gt;            &lt;span class=&#34;nf&#34;&gt;getLikes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;12&lt;/span&gt;          &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;13&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;14&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;15&lt;/span&gt;      &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;k2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;likeSemantics&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;16&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v5&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;17&lt;/span&gt;          &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;18&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;k2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;19&lt;/span&gt;          &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;20&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;21&lt;/span&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;22&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;23&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;24&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;
&lt;span class=&#34;ln&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
        
      </description>
    </item>
    
    <item>
      <title>Top Reddit Demo</title>
      <link>https://tekao.net/posts/top_reddit_demo/</link>
      <pubDate>Tue, 01 Dec 2020 01:48:28 +0000</pubDate>
      
      <guid>https://tekao.net/posts/top_reddit_demo/</guid>
      <description>
        
          &lt;h2 id=&#34;top-reddit-inlineimagesscreenshot202021-02-2320225357pnghttpapptekaonettop_reddit&#34;&gt;&lt;a href=&#34;http://app.tekao.net/top_reddit&#34;&gt;Top Reddit &lt;img src=&#34;https://tekao.net/images/Screenshot%202021-02-23%20225357.png&#34; alt=&#34;:inline&#34;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This runs locally on this server as a Go service running in Docker behind an HAProxy that is routing the URL.&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s page scraping the top links by domain over last 24 hours page on Reddit for selected domains. It uses &lt;a href=&#34;https://github.com/PuerkitoBio/goquery&#34;&gt;GoQuery&lt;/a&gt; to do this which gives you JQuery like syntax in Go to select elements.&lt;/li&gt;
&lt;li&gt;It updates every 30 minutes, you can see when it last ran at the bottom of the page.&lt;/li&gt;
&lt;/ul&gt;

        
      </description>
    </item>
    
    <item>
      <title>Rsync File Matching</title>
      <link>https://tekao.net/posts/rsync_file_matching/</link>
      <pubDate>Tue, 19 Jun 2018 06:26:35 +1200</pubDate>
      
      <guid>https://tekao.net/posts/rsync_file_matching/</guid>
      <description>
        
          &lt;p&gt;&lt;a href=&#34;https://rsync.samba.org/&#34;&gt;Rsync&lt;/a&gt; is a super effective file synchronization tool. It is versatile in how it synchronizes a source file tree to a destination file tree, updating, adding and deleting files. It uses file meta-data and block diffing of contents to provide efficient file transfers. Selecting the list of files to transfer can be difficult, for exmaple doing backups of file trees where you want to exclude some paths.&lt;/p&gt;
&lt;h2 id=&#34;basics&#34;&gt;Basics&lt;/h2&gt;
&lt;p&gt;When transferring files usually what you want to do is select a local directory and specify a remote directory to copy the contents of the local directory into.  In the example below copying the contents of &lt;code&gt;/home/tim/&lt;/code&gt; on local filesystem into &lt;code&gt;/backup/tim_home&lt;/code&gt; on the server.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;ln&#34;&gt;1&lt;/span&gt;rsync -a /home/tim/ server_hostname:/backup/tim_home
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;note&#34;&gt;Note&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tim_home&lt;/code&gt; will be created if it does not already exist on the server in the &lt;code&gt;/backup&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;Note that the trailing slash in the source path &lt;code&gt;/home/tim/&lt;/code&gt; is important without it &lt;code&gt;tim_home/tim&lt;/code&gt; will be created on the server.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rsync -a server_hostname:/backup/tim_home/ /home/tim&lt;/code&gt; is reverse remote to local.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;filter-rules&#34;&gt;Filter Rules&lt;/h2&gt;
&lt;p&gt;Filter rule matching is a big topic in rsync, reading the man page can be useful, but it is very long and can be hard to read. This is a discussion of the most common uses. Rules are made up of patterns that match file paths (file or directories names) in a tree.&lt;/p&gt;
&lt;h3 id=&#34;includeexclude&#34;&gt;Include/Exclude&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;-a&lt;/code&gt; flag to &lt;code&gt;rsync&lt;/code&gt; is archive mode, that almost always should be set when syncing trees, it means that the whole tree of files and subdirectories under the source path will be included.&lt;/p&gt;
&lt;p&gt;To exclude a path use a &lt;code&gt;--exclude=&lt;/code&gt; argument with a pattern, this will override the &lt;code&gt;-a&lt;/code&gt; inclusion. But this too can be overridden by using &lt;code&gt;--include=&lt;/code&gt;. To do this it must precede the &lt;code&gt;--exclude=&lt;/code&gt; on the command line and all parent directories specified in the rule must be included. This is shown in the example below.&lt;/p&gt;
&lt;h3 id=&#34;-and--pattern-operators&#34;&gt;&lt;code&gt;*&lt;/code&gt; and &lt;code&gt;**&lt;/code&gt; pattern operators&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;*&lt;/code&gt; and &lt;code&gt;**&lt;/code&gt; can be used in patterns to match any path. &lt;code&gt;*&lt;/code&gt; will match any path excluding slashes. &lt;code&gt;**&lt;/code&gt; Will also include slashes.&lt;/p&gt;
&lt;h3 id=&#34;transfer-root&#34;&gt;Transfer root&lt;/h3&gt;
&lt;p&gt;If a pattern starts with a &lt;code&gt;/&lt;/code&gt; it will match against transfer root, otherwise it will match from the end of the path. The example matches against transfer root.&lt;/p&gt;
&lt;h3 id=&#34;example&#34;&gt;Example&lt;/h3&gt;
&lt;p&gt;For example let&amp;rsquo;s say you have a directory &lt;code&gt;/home/tim/project&lt;/code&gt; with a sub directory &lt;code&gt;editor&lt;/code&gt; that you want to select but you want exclude all other files and directories in &lt;code&gt;/home/tim/project&lt;/code&gt; from the home directory backup.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;ln&#34;&gt;1&lt;/span&gt;rsync -a --include&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/project/editor** --exclude&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/project/** /home/tim/ server:/backup/tim_home
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;the-important-parts&#34;&gt;The important parts:&lt;/h4&gt;
&lt;p&gt;The include comes first.&lt;/p&gt;
&lt;p&gt;The exclude does not match &lt;code&gt;/project&lt;/code&gt; directory. If the exclude was &lt;code&gt;--exclude=/project**&lt;/code&gt; instead,  &lt;code&gt;/project&lt;/code&gt; would be excluded even with the include rule. Because the include rule only matches the subdirectory, all parents of the directories it matches must be included for it to take effect.&lt;/p&gt;
&lt;p&gt;The root of the patterns is from the perspective of the transfer, since we have a trailing slash in the source &lt;code&gt;/home/tim/&lt;/code&gt; the root is the contents of this directory.&lt;/p&gt;
&lt;h4 id=&#34;note-1&#34;&gt;Note&lt;/h4&gt;
&lt;p&gt;Note that in this example another directory with &amp;ldquo;editor&amp;rdquo; as the prefix would be matched too, this could be fixed by having &lt;code&gt;--include=/code/editor --include=/code/editor/**&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;trying-it-out&#34;&gt;Trying it out&lt;/h2&gt;
&lt;p&gt;Easiest way to try this out is to use the &lt;code&gt;-v&lt;/code&gt; and &lt;code&gt;-n&lt;/code&gt; options to &lt;code&gt;rsync&lt;/code&gt; these add verbosity (v) which lists the files that matched and only do a dry run (n) with no actual transfer,  respectively. Create a small directory tree in a temporary directory and just try to sync it including and excluding different sets of files to see how this works.&lt;/p&gt;

        
      </description>
    </item>
    
    <item>
      <title>AWS EBS Magnetic HDD storage I/O performance</title>
      <link>https://tekao.net/posts/aws_ebs_performance/</link>
      <pubDate>Wed, 31 Aug 2016 02:19:03 +1200</pubDate>
      
      <guid>https://tekao.net/posts/aws_ebs_performance/</guid>
      <description>
        
          


&lt;p&gt;Using &lt;a href=&#34;http://www.coker.com.au/bonnie++/&#34;&gt;http://www.coker.com.au/bonnie++/&lt;/a&gt; as a benchmark. Sequential write performance seems to be what the AWS magnetic EBS is best at.&lt;/p&gt;
&lt;h3&gt;AWS Setup&lt;/h3&gt;
&lt;ul style=&#34;list-style-type: disc;&#34;&gt;
&lt;li&gt;20Gb Magnetic HDD EBS volume&lt;/li&gt;
&lt;li&gt;&lt;code&gt;t1.micro&lt;/code&gt; instance in availability zone: eu-west-1b&lt;/li&gt;
&lt;li&gt;command &lt;code&gt;bonnie++ -s 2G -d /mnt/data/tmp&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Local Setup&lt;/h3&gt;
&lt;ul style=&#34;list-style-type: disc;&#34;&gt;
&lt;li&gt;750Gb Toshiba Magnetic HDD 5200 RPM, 8mb buffer&lt;/li&gt;
&lt;li&gt;Core 2 Duo P7550 2.26Ghz , 8Gb RAM&lt;/li&gt;
&lt;li&gt;command &lt;code&gt;bonnie++ -s 16G -d /tmp&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; The size options here are double the systems RAM, this is to try to prevent reads from disk cache. &lt;/p&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;p&gt;&lt;span style=&#34;color: red;&#34;&gt; AWS EBS&lt;/span&gt; VS &lt;span style=&#34;color: purple;&#34;&gt;Local HDD&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style=&#34;display: block; background: white;&#34; src=&#34;https://tekao.net/images/aws_vs_local.png&#34; alt=&#34;&#34; width=&#34;673&#34; height=&#34;336&#34; /&gt;&lt;/p&gt;
&lt;table style=&#34;font-size: smaller; text-align: right;&#34;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sequential IO Test&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS EBS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Local Magnetic HDD&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write Block&lt;/td&gt;
&lt;td&gt;         &lt;/td&gt;
&lt;td&gt;20,595&lt;/td&gt;
&lt;td&gt;         &lt;/td&gt;
&lt;td&gt;62,549 &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read Block&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;10,571&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;63,776 &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rewrite Block&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;8,109&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;30,285 &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Per Char&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write Per Char&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;17,273&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;585&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read Per Char&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;10,213&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;2,322 &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random Seeks&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;399&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;109.2 &lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Values are in Kb/s except for Random Seeks which is seeks/s.&lt;/p&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;p&gt;Sequential write performance seems to be what the AWS magnetic EBS is best at, it reads at half the rate. Local HDD read/write is more symmetrical.&lt;/p&gt;
&lt;p&gt;Inefficient I/O code (reading/writing one byte each I/O call) really hurts performance on local HDD, but on AWS EBS it seems this is mostly negated by the underlying virtualization technology doing the i/o.&lt;/p&gt;
&lt;p&gt;In general Magnetic EBS is three times slower at writing and six times slower at reading data than a local HDD.&lt;/p&gt;
&lt;p&gt;Not sure about seek performance see the notes below.&lt;/p&gt;
&lt;h3&gt;Notes&lt;/h3&gt;
&lt;p&gt;There are some differences in bonnie++ versions, also the filesystem on the local HDD was nearly full a the time of the tests.&lt;/p&gt;
&lt;h3&gt;Bonnie++ Output&lt;/h3&gt;
&lt;p&gt;AWS:&lt;/p&gt;
&lt;pre style=&#34;font-size: 12px;&#34;&gt;Version 1.03e       ------Sequential Output------ --Sequential Input- --Random-&lt;br /&gt;                    -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--&lt;br /&gt;Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP&lt;br /&gt;ip-172-31-12-48  2G 17273  29 20595   3  8109   1 10213  17 10571   0 399.5   1&lt;br /&gt;                    ------Sequential Create------ --------Random Create--------&lt;br /&gt;                    -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--&lt;br /&gt;              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP&lt;br /&gt;                 16 +++++ +++ +++++ +++ +++++ +++ +++++ +++ +++++ +++ +++++ +++&lt;br /&gt;ip-172-31-12-48,2G,17273,29,20595,3,8109,1,10213,17,10571,0,399.5,1,16,+++++,+++,+++++,+++,+++++,+++,+++++,+++,+++++,+++,+++++,+++&lt;/pre&gt;
&lt;p&gt;Local:&lt;/p&gt;
&lt;pre style=&#34;font-size: 12px;&#34;&gt;Version  1.97       ------Sequential Output------ --Sequential Input- --Random-&lt;br /&gt;Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--&lt;br /&gt;Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP&lt;br /&gt;minerva         16G   585  90 62549  10 30285   5  2322  95 63776   7 109.2   3&lt;br /&gt;Latency             22012us    2442ms    3533ms   19712us   32693us    1609ms&lt;br /&gt;Version  1.97       ------Sequential Create------ --------Random Create--------&lt;br /&gt;minerva             -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--&lt;br /&gt;              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP&lt;br /&gt;                 16 18445  34 +++++ +++ +++++ +++ 27279  49 +++++ +++ 30763  45&lt;br /&gt;Latency             14623us    1209us    1105us    1175us     438us     516us&lt;br /&gt;1.97,1.97,minerva,1,1485665620,16G,,585,90,62549,10,30285,5,2322,95,63776,7,109.2,3,16,,,,,18445,34,+++++,+++,+++++,+++,27279,49,+++++,+++,30763,45,22012us,2442ms,3533ms,19712us,32693us,1609ms,14623us,1209us,1105us,1175us,438us,516us&lt;br /&gt;t&lt;/pre&gt;

        
      </description>
    </item>
    
    <item>
      <title>SWT</title>
      <link>https://tekao.net/posts/swt/</link>
      <pubDate>Wed, 03 Aug 2016 02:28:28 +1200</pubDate>
      
      <guid>https://tekao.net/posts/swt/</guid>
      <description>
        
          


&lt;p&gt;Eclipse SWT cross platform (OSX/Linux/Windows) native controls for Go: &lt;a href=&#34;https://github.com/timob/swt&#34;&gt;https://github.com/timob/swt&lt;/a&gt;. Check the &lt;a href=&#34;https://github.com/timob/swt/blob/master/README.md&#34;&gt;Readme&lt;/a&gt;, on how to get started.&lt;/p&gt;
&lt;p&gt;This package is used by &lt;a href=&#34;https://tekao.net/post/cyan_ide&#34;&gt;Cyan&lt;/a&gt; for its UI. Go&#39;s concurrency, synchronization and function closure features are useful in UI event/callback code. It is very suited to this kind of programming. UIs can be made very responsive, by easily running things in the background from an event handler.&lt;/p&gt;
&lt;p&gt;Having a native Go UI option would be better, but this will take time.&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://tekao.net/images/widgets.png&#34;&gt;&lt;img style=&#34;display: block; margin-left: auto; margin-right: auto;&#34; src=&#34;https://tekao.net/images/widgets.png&#34; alt=&#34;&#34; width=&#34;137&#34; height=&#34;179&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Demonstrates adding tab and tree controls to a window. (from examples dir)&lt;/p&gt;
&lt;!--
&lt;pre style=&#34;font-size: 12px; height: 300px; overflow-y: scroll;&#34;&gt;
--&gt;
&lt;pre&gt;package main

import (
	&#34;github.com/timob/javabind&#34;
	&#34;github.com/timob/swt&#34;
	&#34;log&#34;
	&#34;os&#34;
)

// Example as shown at https://www.eclipse.org/swt/

func main() {
	err := javabind.SetupJVM(os.Getenv(&#34;CLASSPATH&#34;))
	if err != nil {
		log.Fatal(err)
	}

	var SWT *swt.SWT

	display := swt.NewWidgetsDisplay()
	shell := swt.NewWidgetsShell3(display)
	shell.SetLayout(swt.NewLayoutGridLayout2(2, true))

	tabFolder := swt.NewWidgetsTabFolder(shell, SWT.NONE())
	tabFolder.SetLayoutData(swt.NewLayoutGridData5(SWT.FILL(), SWT.FILL(), true, true, 2, 1))
	item := swt.NewWidgetsTabItem(tabFolder, SWT.NONE())
	item.SetText(&#34;Widget&#34;)

	composite := swt.NewWidgetsComposite(tabFolder, SWT.NONE())
	composite.SetLayout(swt.NewLayoutGridLayout())

	tree := swt.NewWidgetsTree(composite, SWT.BORDER())
	item.SetControl(composite)
	tree.SetHeaderVisible(true)
	tree.SetLayoutData(swt.NewLayoutGridData4(SWT.FILL(), SWT.FILL(), true, true))
	column1 := swt.NewWidgetsTreeColumn(tree, SWT.NONE())
	column1.SetText(&#34;Standard&#34;)
	column2 := swt.NewWidgetsTreeColumn(tree, SWT.NONE())
	column2.SetText(&#34;Widget&#34;)
	branch := swt.NewWidgetsTreeItem(tree, SWT.NONE())
	branch.SetText2([]string{&#34;Efficient&#34;, &#34;Portable&#34;})
	leaf := swt.NewWidgetsTreeItem2(branch, SWT.NONE())
	leaf.SetText2([]string{&#34;Cross&#34;, &#34;Platform&#34;})
	branch.SetExpanded(true)
	branch = swt.NewWidgetsTreeItem(tree, SWT.NONE())
	branch.SetText2([]string{&#34;Native&#34;, &#34;Controls&#34;})
	leaf = swt.NewWidgetsTreeItem2(branch, SWT.NONE())
	leaf.SetText2([]string{&#34;Cross&#34;, &#34;Platform&#34;})
	branch = swt.NewWidgetsTreeItem(tree, SWT.NONE())
	branch.SetText2([]string{&#34;Cross&#34;, &#34;Platform&#34;})
	column1.Pack()
	column2.Pack()

	item = swt.NewWidgetsTabItem(tabFolder, SWT.NONE())
	item.SetText(&#34;Toolkit&#34;)

	button := swt.NewWidgetsButton(shell, SWT.CHECK())
	button.SetText(&#34;Totally&#34;)
	button.SetSelection(true)
	button.SetLayoutData(swt.NewLayoutGridData4(SWT.CENTER(), SWT.CENTER(), false, false))
	listener := NewSelectionListener(button)

	button = swt.NewWidgetsButton(shell, SWT.PUSH())
	button.SetText(&#34;Awesome&#34;)
	button.SetLayoutData(swt.NewLayoutGridData4(SWT.CENTER(), SWT.CENTER(), false, false))

	shell.Pack()
	shell.Open()

	for !shell.IsDisposed() {
		if !display.ReadAndDispatch() {
			display.Sleep()
		}
	}
}

&lt;/pre&gt;
&lt;!--
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;
--&gt;
&lt;h3&gt;Example apps:&lt;/h3&gt;
&lt;p&gt;AWS Plus: small utility to work with AWS&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://tekao.net/images/awsplus.png&#34;&gt;&lt;img style=&#34;border: 5px solid black; display: block; margin-left: auto; margin-right: auto;&#34; src=&#34;https://tekao.net/images/awsplus.png&#34; alt=&#34;&#34; width=&#34;369&#34; height=&#34;204&#34; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt; Windows 10 &lt;/h3&gt;
&lt;a href=&#34;https://tekao.net/images/Annotation 2019-05-29 012105.png&#34;&gt;&lt;img style=&#34;border: 5px solid black; display: block; margin-left: auto; margin-right: auto;&#34; src=&#34;https://tekao.net/images/Annotation 2019-05-29 012105.png&#34; width=&#34;369&#34; height=&#34;204&#34;/&gt;&lt;/a&gt;


        
      </description>
    </item>
    
    <item>
      <title>Cyan IDE</title>
      <link>https://tekao.net/posts/cyan_ide/</link>
      <pubDate>Fri, 29 Jul 2016 11:05:05 +1200</pubDate>
      
      <guid>https://tekao.net/posts/cyan_ide/</guid>
      <description>
        
          

&lt;p&gt;Cyan is an upcoming Go IDE/Editor written in Go. Designed with a minimalist / responsive UI. It&#39;s designed not interfere with your CLI tools.&lt;a href=&#34;https://tekao.net/images/cyan_screenshot.png&#34;&gt;&lt;img style=&#34;border: 5px solid black; margin-top: 7px; display: block; margin-left: auto; margin-right: auto;&#34; src=&#34;https://tekao.net/images/cyan_screenshot.png&#34; alt=&#34;&#34; width=&#34;294&#34; height=&#34;189&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I use this daily for coding in Go while developing it along the way, so the features at the moment are what I find I most need. All the UI components are keyboard navigatable (why use a mouse?). The file navigation is inspired from IntelliJ, access to the directory where you are working, with quick navigation to other directories from the buttons on top. All the undo history is written in Go, ie. if you save edit then undo back to save point it won&#39;t ask you to save. The scrolling is done with Go callbacks to give you nice clamped at 60 fps scrolling. Dialog boxes get in the way so are not there, all messages are shown in responsive way. The design is to use Go routines to do all non-interactive tasks in the background and prioritize user input. What&#39;s shown here is just the start.&lt;/p&gt;
&lt;p&gt;It uses &lt;a href=&#34;https://github.com/timob/jnigi&#34;&gt;JNIGI&lt;/a&gt; to use Java which is used for &lt;a href=&#34;https://github.com/timob/swt&#34;&gt;SWT&lt;/a&gt; UI components.&lt;/p&gt;
&lt;h3&gt;Features&lt;/h3&gt;
&lt;ul style=&#34;list-style-type: disc;&#34;&gt;
&lt;li&gt;Cross platform Linux/Windows/OSX&lt;/li&gt;
&lt;li&gt;Syntax highlighting using core Go parser&lt;/li&gt;
&lt;li&gt;Persistent saving of application state over restarts&lt;/li&gt;
&lt;li&gt;File browser, quick access to directories in the current file path&lt;/li&gt;
&lt;li&gt;Search/Replace&lt;/li&gt;
&lt;li&gt;Smart indentation, home&lt;/li&gt;
&lt;li&gt;UTF 8 support&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Download&lt;/h3&gt;
&lt;p&gt;coming soon&lt;/p&gt;
&lt;h3&gt;Future&lt;/h3&gt;
&lt;ul style=&#34;list-style-type: disc;&#34;&gt;
&lt;li&gt;Improved syntax highlighting (right now it limits knowledge to current file)&lt;/li&gt;
&lt;li&gt;Always on Gofmt&lt;/li&gt;
&lt;li&gt;Auto sync of editor buffers with files on the filesystem&lt;/li&gt;
&lt;li&gt;Code completion&lt;/li&gt;
&lt;li&gt;Indicate compiler errors in editor&lt;/li&gt;
&lt;li&gt;Debugger with breakpoint support&lt;/li&gt;
&lt;li&gt;Custom code analysis / scripting&lt;/li&gt;
&lt;li&gt;Source code navigation / sidebar code outlines&lt;/li&gt;
&lt;li&gt;Multi file refactoring, renaming etc..&lt;/li&gt;
&lt;li&gt;Display Git revisions/branches&lt;/li&gt;
&lt;/ul&gt;

        
      </description>
    </item>
    
    <item>
      <title>Android  USB Network Connection</title>
      <link>https://tekao.net/posts/android_usb/</link>
      <pubDate>Thu, 23 Jun 2016 08:43:03 +1200</pubDate>
      
      <guid>https://tekao.net/posts/android_usb/</guid>
      <description>
        
          


&lt;p&gt;It&#39;s possible to create a network connection between an Android phone and your computer over USB. Also some later Android versions have this option in the settings menu. This method uses the &lt;a href=&#34;https://en.wikipedia.org/wiki/RNDIS&#34;&gt;RNDIS&lt;/a&gt; protocol on a a jail broken phone. This information is available elsewhere but it&#39;s somewhat hard to find. So I am reposting here.&lt;/p&gt;
&lt;pre&gt;echo 0 &gt; /sys/class/android_usb/android0/enable
echo 18d1 &gt; /sys/class/android_usb/android0/idVendor
echo 4e23 &gt; /sys/class/android_usb/android0/idProduct
echo rndis &gt; /sys/class/android_usb/android0/functions
echo 224 &gt; /sys/class/android_usb/android0/bDeviceClass
echo 1 &gt; /sys/class/android_usb/android0/enable
setprop sys.usb.state rndis
netcfg rndis0 up
ifconfig rndis0 10.100.100.1 netmask 255.255.255.0
&lt;/pre&gt;
&lt;p&gt; This is the code that works on my Samsung Galaxy Nexus (yes a very old phone) running Android 4.3. I imagine the process is similar on other phones, but maybe the Vendor/Product codes need to change.&lt;/p&gt;
&lt;p&gt;After the above script is run on your phone just connect it&#39;s USB cable to your computer. On Linux the interface will show up as for example usb0. I think the interface is provided by the rndis_host, cdc_ether and usbnet kernel modules which hopefully load automatically. Just configure it with a corresponding address on the subnet and you have a IP over USB to your phone.&lt;/p&gt;
&lt;p&gt;Useful for sharing your phones 3g/Wifi or app development/hacking.&lt;/p&gt;

        
      </description>
    </item>
    
    <item>
      <title>Let&#39;s Encrypt</title>
      <link>https://tekao.net/posts/lets_encrypt/</link>
      <pubDate>Thu, 05 May 2016 08:50:59 +1200</pubDate>
      
      <guid>https://tekao.net/posts/lets_encrypt/</guid>
      <description>
        
          


&lt;p&gt;Tekao.net now uses &lt;a href=&#34;https://letsencrypt.org/&#34;&gt;Letsencrypt&lt;/a&gt;! Let&#39;s Encrypt issues short term certificates, three months i think, for websites that can prove they own a domain. You can check the letsencrypt.org certificate for the page you are reading now by clicking on the padlock to left of the address bar in your browser.&lt;/p&gt;
&lt;p&gt;Using &lt;a href=&#34;https://rsc.io/letsencrypt&#34;&gt;rsc.io/letsencrypt&lt;/a&gt; it&#39;s extremely simple to have a Golang webserver that uses the standard HTTP package be supported. It all happens on demand with no configuration: Given a HTTPS request to your server, it will ask for a certificate and prove to Let&#39;s Encrypt that you own the host whose name is specified in the request. It proves this by serving specific content reachable from Let&#39;s Encrypt servers using the host name.&lt;/p&gt;
&lt;p&gt;This certificate will be used on subsequent requests. I guess until it expires.&lt;/p&gt;
&lt;p&gt;Check out Let&#39;s Encrypt website for details of the certificate issuing protocol.&lt;/p&gt;
&lt;p&gt;Very cool.&lt;/p&gt;

        
      </description>
    </item>
    
    <item>
      <title>Modern ls</title>
      <link>https://tekao.net/posts/ls/</link>
      <pubDate>Thu, 25 Feb 2016 08:18:08 +1300</pubDate>
      
      <guid>https://tekao.net/posts/ls/</guid>
      <description>
        
          


&lt;p&gt;List directory Unix utility written in pure Go. Has some extra options: Humanized relative timestamps, path mode and cross platform.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&#34;I have it in my PATH!&#34;&lt;/em&gt;👍&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/timob/ls&#34;&gt;https://github.com/timob/ls&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some Features:&lt;/p&gt;
&lt;ul style=&#34;list-style-type: disc;&#34;&gt;
&lt;li&gt;Compatiblity with GNU ls&lt;/li&gt;
&lt;li&gt;-R has changed, meaning recursively lists all files with relative path names as one group. This means sort options such as by time (-t) can show most recent file modified under a path. (Useful for example to see latest logs in /var/log)&lt;/li&gt;
&lt;li&gt;-O only list entries starting with .&lt;/li&gt;
&lt;li&gt;Color output&lt;/li&gt;
&lt;li&gt;Works on Linux/Windows/OSX&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Screenshot showing -1PRtr options in effect, showing 1 column, only file paths, recursive subdir, sort by time reversed:&lt;/p&gt;
&lt;p style=&#34;overflow-x: hidden;&#34;&gt;&lt;img src=&#34;https://tekao.net/images/ls_ss.png&#34; alt=&#34;&#34; width=&#34;863&#34; height=&#34;327&#34; /&gt;&lt;/p&gt;
&lt;p style=&#34;overflow-x: hidden;&#34;&gt;Showing now relative Humanized timestamps for last modified:&lt;/p&gt;
&lt;p style=&#34;overflow-x: hidden;&#34;&gt;&lt;img src=&#34;https://tekao.net/images/ls2.png&#34; alt=&#34;&#34; width=&#34;1026&#34; height=&#34;208&#34; /&gt;&lt;/p&gt;

&lt;p style=&#34;overflow-x: hidden;&#34;&gt;Improved layout:&lt;/p&gt;

&lt;p style=&#34;overflow-x: hidden;&#34;&gt;&lt;img src=&#34;https://tekao.net/images/ls_compact.png&#34; alt=&#34;&#34; width=&#34;1026&#34; height=&#34;208&#34; /&gt;&lt;/p&gt;

&lt;p style=&#34;overflow-x: hidden;&#34;&gt;Compared to standard MacOS ls:&lt;/p&gt;

&lt;p style=&#34;overflow-x: hidden;&#34;&gt;&lt;img src=&#34;https://tekao.net/images/macos_ls.png&#34; alt=&#34;&#34; width=&#34;1026&#34; height=&#34;208&#34; /&gt;&lt;/p&gt;

        
      </description>
    </item>
    
    <item>
      <title>Sindex</title>
      <link>https://tekao.net/posts/sindex/</link>
      <pubDate>Thu, 25 Feb 2016 05:42:40 +1300</pubDate>
      
      <guid>https://tekao.net/posts/sindex/</guid>
      <description>
        
          
&lt;p&gt;
Slice Indexing library for Golang. SIndex is a slice indexing library. It maintains an ordered list, by mapping list positions to slice indexes. sindex.Interface implementation types: List (Basic slice list), LinkedList (Todo)
&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/timob/sindex&#34;&gt;https://github.com/timob/sindex&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Documentation&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://godoc.org/github.com/timob/sindex&#34;&gt;https://godoc.org/github.com/timob/sindex&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;&lt;a id=&#34;user-content-example&#34; class=&#34;anchor&#34; href=&#34;https://github.com/timob/sindex#example&#34;&gt;&lt;/a&gt;Example&lt;/h4&gt;
&lt;div class=&#34;highlight highlight-source-go&#34; style=&#34;font-size: smaller;&#34;&gt;
&lt;pre&gt;&lt;span class=&#34;pl-k&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;pl-en&#34;&gt;ExampleList&lt;/span&gt;() {
    &lt;span class=&#34;pl-smi&#34;&gt;bytes&lt;/span&gt; &lt;span class=&#34;pl-k&#34;&gt;:=&lt;/span&gt; []&lt;span class=&#34;pl-k&#34;&gt;byte&lt;/span&gt;(&lt;span class=&#34;pl-s&#34;&gt;&lt;span class=&#34;pl-pds&#34;&gt;&#34;&lt;/span&gt;helloworld&lt;span class=&#34;pl-pds&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;)
    &lt;span class=&#34;pl-smi&#34;&gt;bl&lt;/span&gt; &lt;span class=&#34;pl-k&#34;&gt;:=&lt;/span&gt; sindex.&lt;span class=&#34;pl-c1&#34;&gt;NewList&lt;/span&gt;(&amp;bytes)
    bytes[bl.&lt;span class=&#34;pl-c1&#34;&gt;Insert&lt;/span&gt;(&lt;span class=&#34;pl-c1&#34;&gt;5&lt;/span&gt;)] = &lt;span class=&#34;pl-s&#34;&gt;&lt;span class=&#34;pl-pds&#34;&gt;&#39;&lt;/span&gt; &lt;span class=&#34;pl-pds&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;
    bytes[bl.&lt;span class=&#34;pl-c1&#34;&gt;Append&lt;/span&gt;()] = &lt;span class=&#34;pl-s&#34;&gt;&lt;span class=&#34;pl-pds&#34;&gt;&#39;&lt;/span&gt;!&lt;span class=&#34;pl-pds&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;

    fmt.&lt;span class=&#34;pl-c1&#34;&gt;Println&lt;/span&gt;(&lt;span class=&#34;pl-k&#34;&gt;string&lt;/span&gt;(bytes))

    &lt;span class=&#34;pl-k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;pl-smi&#34;&gt;iter&lt;/span&gt; &lt;span class=&#34;pl-k&#34;&gt;:=&lt;/span&gt; bl.&lt;span class=&#34;pl-c1&#34;&gt;Iterator&lt;/span&gt;(&lt;span class=&#34;pl-c1&#34;&gt;0&lt;/span&gt;); iter.&lt;span class=&#34;pl-c1&#34;&gt;Next&lt;/span&gt;(); {
        fmt.&lt;span class=&#34;pl-c1&#34;&gt;Print&lt;/span&gt;(&lt;span class=&#34;pl-k&#34;&gt;string&lt;/span&gt;(bytes[iter.&lt;span class=&#34;pl-c1&#34;&gt;Pos&lt;/span&gt;()]))
    }

    &lt;span class=&#34;pl-c&#34;&gt;// Output:&lt;/span&gt;
    &lt;span class=&#34;pl-c&#34;&gt;// hello world!&lt;/span&gt;
    &lt;span class=&#34;pl-c&#34;&gt;// hello world!&lt;/span&gt;
}&lt;/pre&gt;
&lt;/div&gt;

        
      </description>
    </item>
    
    <item>
      <title>Nvidia proprietary drivers and Ubuntu 12.04 redraw</title>
      <link>https://tekao.net/posts/nvidia_propriety_drivers/</link>
      <pubDate>Fri, 20 Jun 2014 16:38:48 -0700</pubDate>
      
      <guid>https://tekao.net/posts/nvidia_propriety_drivers/</guid>
      <description>
        
          


&lt;p&gt;
One thing that took me a long time to fix was, small of regions windows not getting redrawn when modified. After trying countless things, the option was in compiz config settings manager (ccsm).
&lt;/p&gt;


To fix, in CCSM-&gt;Workarounds it i set:&lt;br /&gt;&#34;Force complete redraw on initial damage&#34;&lt;br /&gt;&#34;Force fullscreen redraws (buffer swap) on repaint&#34;&lt;br /&gt;&lt;br /&gt;Now this works very nicely indeed....&lt;br /&gt;&lt;br /&gt;Thanks to all the people working on Ubuntu/Linux software!!!&lt;/p&gt;

&lt;p&gt;&lt;span style=&#34;color: red;&#34;&gt;Update:&lt;/span&gt;&lt;br /&gt;Using nvidia-settings, changing power saving mode to maximum performance fixes this problem. It gives better performance than below but i assume uses more power. &lt;br /&gt;&lt;br /&gt;

        
      </description>
    </item>
    
    <item>
      <title>Tweaking Ubuntu 12.04 to run on a Macbook</title>
      <link>https://tekao.net/posts/tweaking_ubuntu_macbook/</link>
      <pubDate>Mon, 05 Aug 2013 01:28:11 -0700</pubDate>
      
      <guid>https://tekao.net/posts/tweaking_ubuntu_macbook/</guid>
      <description>
        
          


&lt;p&gt; Configuration tweaks for Ubuntu. &lt;/p&gt;


&lt;p&gt;&lt;a style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34; href=&#34;https://tekao.net/images/IMG_20121202_201211.jpg&#34;&gt;&lt;img src=&#34;https://tekao.net/images/IMG_20121202_201211.jpg&#34; alt=&#34;&#34; width=&#34;240&#34; height=&#34;320&#34; border=&#34;0&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;
&lt;h3&gt;&lt;span style=&#34;color: red;&#34;&gt;Update 2&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;Ubuntu looks better with it&#39;s default color profile. Probably not worth changing. &lt;/li&gt;
&lt;li&gt;With the latest NVidia drivers I&#39;ve found the options I set below are not needed (apart from the brightness one). &lt;/li&gt;
&lt;li&gt;Also see new post on how to get Nvidia/Compiz to work together nicely.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;span style=&#34;color: red;&#34;&gt;Updated&lt;/span&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;After using the above setup for a while, I&#39;ve updated some things.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I found you can&#39;t really replicate the way the fonts look in OSX, so it&#39;s better to set your font configuration to using hinting (I am using medium hinting). This makes the fonts look crisper more like Windows. With this setting using a default of DejaVu Sans as your default Sans font in Chrome looks good. In fact most of the sans fonts that are installed by default look better with some hinting.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;The trackpad mtrack driver works much better if you install it from source:&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;sudo apt-get build-dep xserver-xorg-input-mtrack&lt;/div&gt;
&lt;p&gt;Download the source from &lt;a href=&#34;https://github.com/BlueDragonX/xf86-input-mtrack/&#34;&gt;https://github.com/BlueDragonX/xf86-input-mtrack/&lt;/a&gt; .&lt;/p&gt;
&lt;blockquote class=&#34;tr_bq&#34;&gt;cd xf86-input-mtrack/&lt;br /&gt;libtoolize&lt;br /&gt;aclocal&lt;br /&gt;autoconf&lt;br /&gt;automake -c --add-missing&lt;br /&gt;./configure&lt;br /&gt;make&lt;/blockquote&gt;
&lt;p&gt;I then just replaced the driver&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;blockquote class=&#34;tr_bq&#34;&gt;sudo mv .libs/mtrack_drv.so /usr/lib/xorg/modules/input/&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Original post:&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I have a 2009 Macbook its model number 6,2. Running Ubuntu 12.04 Precise Pangolin.&lt;br /&gt;&lt;br /&gt;The OS install is pretty straightforward. The instructions are &lt;a href=&#34;https://help.ubuntu.com/community/MactelSupportTeam/AppleIntelInstallation&#34;&gt;here&lt;/a&gt;. Notes on those install instructions:&lt;/p&gt;
&lt;ul style=&#34;list-style-type: disc;&#34;&gt;
&lt;li&gt;You have to burn a CD, booting off of USB never worked from me.&lt;/li&gt;
&lt;li&gt;I used the specific install ISO for 64bit Mac computers. Available &lt;a href=&#34;http://cdimage.ubuntu.com/releases/12.04/release/&#34;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Ubuntu installer asks you to create an an extra parition for booting, in addition to the normal filesystem/swap parititons.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;At the end you should end up with a working Ubuntu Install. But here are some tweaks to make the hardware work well.&lt;/div&gt;
&lt;h3&gt;Graphics card&lt;/h3&gt;
&lt;div&gt;The model I have has a NVidia 9400M graphics card which shares memory with system RAM. The Nouveau drivers as well as the NVidia driver version that is installed by default cause instability after a while resulting in lockups. I ended up installing the latest Linux driver from the NVidia website. I am using version 304.37. This worked well but I still had some X server crashes, while not as bad as complete lock ups are still annoying. I&#39;ve disabled some options in my xorg.conf in the nvidia device section:&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&#34;tr_bq&#34;&gt;Option &#34;IndirectMemoryAccess&#34; &#34;Off&#34;&lt;br /&gt;Option &#34;NoFlip&#34; &#34;True&#34;&lt;br /&gt;Option &#34;DynamicTwinView&#34; &#34;Off&#34;&lt;/blockquote&gt;
This seems to make my system more stable. I also added:&lt;br /&gt;
&lt;blockquote class=&#34;tr_bq&#34;&gt;Option &#34;RegistryDwords&#34; &#34;EnableBrightnessControl=1&#34;&lt;/blockquote&gt;
To fix the brightness controls.&lt;br /&gt;If the Nouveau driver works for you I would stick to this.&lt;br /&gt;
&lt;h3&gt;Display&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;By default the color profile of the screen is bluish, different from the warmer profile in OSX. To get the same color profile, boot OSX and save the default color profile to an ICC file. This can be done by using system preferences -&gt; display -&gt; color -&gt; open profile -&gt; save as. Then you can load this file in Ubuntu, I used the xcalib utility to do this, available from the apt repositories.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I found the fonts by default didn&#39;t look as good as on OSX, especially in the web browser using the Macbook laptop display. The best results I achieved was to use grayscale anti-antialiasing, except for font sizes 13 point and smaller which I set to use subpixel anti-aliasing. And to not use any hinting. The second thing was to set the default sans-serif font in the Chrome web browser to &#34;Nimbus Sans L&#34; this seems most like Helvetica/Arial used on the web alot. (See Update below.)&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I configured the fonts using /etc/fonts/conf.d. Here is my listing of that directory:&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&#34;tr_bq&#34;&gt;
&lt;blockquote class=&#34;tr_bq&#34;&gt;&lt;span style=&#34;font-family: Courier New, Courier, monospace; font-size: xx-small;&#34;&gt;10-no-sub-pixel.conf 10-unhinted.conf 11-lcd-filter-lcddefault.conf 20-fix-globaladvance.conf 20-unhint-small-vera.conf 30-subpixel-for-small.conf 30-urw-aliases.conf 40-nonlatin.conf 45-latin.conf 49-sansserif.conf 50-user.conf 51-local.conf 53-monospace-lcd-filter.conf 60-latin.conf 64-01-tlwg-kinnari.conf 64-02-tlwg-norasi.conf 64-11-tlwg-waree.conf 64-12-tlwg-loma.conf 64-13-tlwg-garuda.conf 64-14-tlwg-umpush.conf 64-21-tlwg-typo.conf 64-22-tlwg-typist.conf 64-23-tlwg-mono.conf 65-fonts-persian.conf 65-khmer.conf 65-nonlatin.conf 69-unifont.conf 70-force-bitmaps.conf 80-delicious.conf 89-tlwg-garuda-synthetic.conf 89-tlwg-kinnari-synthetic.conf 89-tlwg-loma-synthetic.conf 89-tlwg-umpush-synthetic.conf 89-tlwg-waree-synthetic.conf 90-fonts-nanum.conf 90-synthetic.conf 90-ttf-bengali-fonts.conf 90-ttf-devanagari-fonts.conf 90-ttf-gujarati-fonts.conf 90-ttf-kannada-fonts.conf 90-ttf-malayalam-fonts.conf 90-ttf-oriya-fonts.conf 90-ttf-punjabi-fonts.conf 90-ttf-tamil-fonts.conf 90-ttf-telugu-fonts.conf 99pdftoopvp.conf README&lt;/span&gt;&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;span style=&#34;font-family: inherit;&#34;&gt;The only c&lt;/span&gt;ustom file in that directory is 30-subpixel-for-small.conf which contains:&lt;/div&gt;
&lt;pre&gt;    &lt;fontconfig&gt;&lt;br /&gt;    &lt;match target=&#34;font&#34;&gt;&lt;br /&gt;        &lt;test name=&#34;size&#34; compare=&#34;less_eq&#34;&gt;&lt;br /&gt;            &lt;double&gt;13&lt;/double&gt;&lt;br /&gt;        &lt;/test&gt;&lt;br /&gt;        &lt;edit name=&#34;rgba&#34; mode=&#34;assign&#34;&gt;&lt;const&gt;rgb&lt;/const&gt;&lt;/edit&gt;&lt;br /&gt;    &lt;/match&gt;&lt;br /&gt;    &lt;/fontconfig&gt;&lt;/pre&gt;
&lt;div&gt;&lt;a style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34; href=&#34;https://tekao.net/images/Screenshot_from_2012-08-31_18_18_03.png&#34;&gt;&lt;img src=&#34;https://tekao.net/images/Screenshot_from_2012-08-31_18_18_03.png&#34; alt=&#34;&#34; width=&#34;320&#34; height=&#34;268&#34; border=&#34;0&#34; /&gt;&lt;/a&gt;I also used the myunity utility ovailable from the repositories to configure the fonts using the GUI to match the fontconfig files. I just set the Hinting to none and Antialiasing to grayscale.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;h3&gt;Trackpad&lt;/h3&gt;
&lt;div&gt;The default Xorg synaptics driver was not working that well, it was too sensitive and didn&#39;t handle multitouch well. I ended up removing the xserver-xorg-input-synaptics package and installing the xserver-xorg-input-mtrack using apt. &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;This driver has a configuration file at /usr/share/X11/xorg.conf.d/50-mtrack.conf. &lt;/div&gt;
&lt;div&gt;I am still tweaking the options here, but the trackpad is ok to use now.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;Day to day use&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;So due to some stupidity on my part my OSX partiton doesn&#39;t boot any more. So I am using my Ubuntu/Mac every day, and I think it&#39;s interface is nearly as good as OSX. That combined with IMHO a better underlying infrastructure in Linux ie system/network software, open file systems and better compatibility and user software that works for you, like being able to copy songs from your Ipod, makes the system on balance better than OSX/Mac.&lt;/div&gt;

        
      </description>
    </item>
    
    <item>
      <title>Macbook RAID</title>
      <link>https://tekao.net/posts/macbook_raid/</link>
      <pubDate>Wed, 08 May 2013 02:18:40 -0700</pubDate>
      
      <guid>https://tekao.net/posts/macbook_raid/</guid>
      <description>
        
          


&lt;p&gt;With some Macbooks it is possible to remove the DVD drive and install a second HDD. You need to buy a slimline SATA adapter to attach the HDD to the DVD power cable. &lt;/p&gt;

&lt;a href=&#34;http://www.amazon.com/Micro-SATA-Cables-Slimline-Adapter/dp/B003YVP8VS/ref=aag_m_pw_dp?ie=UTF8&amp;m=A1UCLUF7KW7AYG&#34;&gt;http://www.amazon.com/Micro-SATA-Cables-Slimline-Adapter/dp/B003YVP8VS/ref=aag_m_pw_dp?ie=UTF8&amp;m=A1UCLUF7KW7AYG&lt;/a&gt;  &lt;/p&gt;

&lt;br /&gt;&lt;br /&gt;I&#39;ve installed a 128GB Vertex 4 SSD and a 750GB HDD Drive in my Macbook using the above method. I&#39;ve partitioned the HDD into a 128GB  partition and another partition with the remaining space (actually there is a small 100mb boot partition on each drive too). I&#39;ve then setup a Hybrid SSD/HDD RAID 1 array as described here &lt;a href=&#34;http://www.tansi.org/hybrid/&#34;&gt;http://www.tansi.org/hybrid/&lt;/a&gt;. This method gives you the read speeds of an SSD with the reliability of RAID 1. &lt;br /&gt;&lt;br /&gt;On the RAID device i have my system/home filesystems, and then on the big non-RAID partition on the HDD i&#39;ve got my big media files/data files etc. This setup is really nice.&lt;br /&gt;&lt;br /&gt;Once thing I&#39;ve notice though is sometimes when a write happens to the RAID device i get some IOwaits if i am reading from the non-RAID part of the HDD. This tends to be noticeable when playing a movie. So to get around this you can set the read-ahead for the device using hdparm. Eg hdparm -a 1024 /dev/sdb. This seems to fix the problem as enough data is present for the movie player to keep reading data while the IOwaits happen.

        
      </description>
    </item>
    
    <item>
      <title>Indexing time periods using MySQL and GNU date</title>
      <link>https://tekao.net/posts/indexing_time_periods_mysql_date/</link>
      <pubDate>Tue, 28 Feb 2012 17:38:29 -0800</pubDate>
      
      <guid>https://tekao.net/posts/indexing_time_periods_mysql_date/</guid>
      <description>
        
          


&lt;p&gt;Periodic data in a database is something i use quite a bit. The problem is using a date time column or a date and period column for each table is quite cumbersome. To make things easier create a time dimension table:&lt;/p&gt;
&lt;div&gt; &lt;/div&gt;
&lt;pre&gt;CREATE TABLE time (&lt;br /&gt;  time_id mediumint(8) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;  date date NOT NULL,&lt;br /&gt;  tp tinyint(3) unsigned NOT NULL,&lt;br /&gt;  year smallint(5) unsigned NOT NULL,&lt;br /&gt;  month tinyint(3) unsigned NOT NULL,&lt;br /&gt;  day_of_year smallint(5) unsigned NOT NULL,&lt;br /&gt;  day_of_week tinyint(3) unsigned NOT NULL,&lt;br /&gt;  PRIMARY KEY (time_id),&lt;br /&gt;  KEY date (date) USING BTREE&lt;br /&gt;);&lt;/pre&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;This indexes every date, and tp column to a time_id. In this case tp is a number from 1 to 48 for half hour periods through out the day. The rest of the columns are use to make queries using time clauses on the data easier.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;To populate this table, use the excellent gnu date command to specify a starting date and a count of the number of days and a format string that can be used to insert into the database. Here are the commands typed into bash to create the sql file to load into the table:&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;div&gt;
&lt;pre&gt;$ daycount=$((366*17))&lt;br /&gt;$ tpcount=48&lt;br /&gt;$ { for ((i=0;$i&lt;$daycount;i=$i+1)); do &lt;br /&gt;          for ((j=1;j&lt;=$tpcount;j=$j+1)); do &lt;br /&gt;              gdate -d &#34;1996-01-01 + $i days&#34; +&#34;insert into time (date, tp, year, month, day_of_year, day_of_week) values (&#39;%Y-%m-%d&#39;, $j, %Y, %m, %j, %u);&#34;; &lt;br /&gt;          done; &lt;br /&gt;      done } &gt; time_data.sql&lt;/pre&gt;
&lt;pre&gt; &lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;This creates a file with insert statements for 17 years starting from 1996-01-01 for every half hour. After loading into the database, a check is to see how many periods / year there are:&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;mysql&gt; select year, count(1) from time group by year order by year;&lt;br /&gt;+------+----------+&lt;br /&gt;| year | count(1) |&lt;br /&gt;+------+----------+&lt;br /&gt;| 1996 |    17568 |&lt;br /&gt;| 1997 |    17520 |&lt;br /&gt;| 1998 |    17520 |&lt;br /&gt;| 1999 |    17520 |&lt;br /&gt;| 2000 |    17568 |&lt;br /&gt;| 2001 |    17520 |&lt;br /&gt;| 2002 |    17520 |&lt;br /&gt;| 2003 |    17520 |&lt;br /&gt;| 2004 |    17568 |&lt;br /&gt;| 2005 |    17520 |&lt;br /&gt;| 2006 |    17520 |&lt;br /&gt;| 2007 |    17520 |&lt;br /&gt;| 2008 |    17568 |&lt;br /&gt;| 2009 |    17520 |&lt;br /&gt;| 2010 |    17520 |&lt;br /&gt;| 2011 |    17520 |&lt;br /&gt;| 2012 |    17568 |&lt;br /&gt;| 2013 |      576 |&lt;br /&gt;+------+----------+&lt;br /&gt;18 rows in set (0.11 sec)&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;Here the leap years have 48 more periods.&lt;br /&gt;Given this price, quantity test data for the first 3 months of the year 2000:&lt;br /&gt;&lt;br /&gt;
&lt;pre&gt;create table price (time_id mediumint not null, price decimal(8,2));&lt;br /&gt;create table quantity (time_id mediumint not null, quantity int);&lt;br /&gt;&lt;br /&gt;insert into price select time_id, cast(rand()*1000 as decimal(8,2)) from dim.time where year = 2000 and month in (1,2,3);&lt;br /&gt;insert into quantity select time_id, round(rand()*1000) from dim.time where year = 2000 and month in (1,2,3);&lt;/pre&gt;
&lt;br /&gt;Queries like this make it easy to get information:&lt;br /&gt;&lt;br /&gt;
&lt;pre&gt;mysql&gt; select date, tp, price*quantity cost from price natural join quantity natural join dim.time order by date,tp limit 3;&lt;br /&gt;+------------+----+-----------+&lt;br /&gt;| date       | tp | cost      |&lt;br /&gt;+------------+----+-----------+&lt;br /&gt;| 2000-01-01 |  1 | 111661.86 |&lt;br /&gt;| 2000-01-01 |  2 | 452187.36 |&lt;br /&gt;| 2000-01-01 |  3 |  57724.85 |&lt;br /&gt;+------------+----+-----------+&lt;br /&gt;3 rows in set (1.28 sec)&lt;/pre&gt;
&lt;div&gt;or:&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;select date, tp, a.price, b.price, round((b.price / a.price),2) * 100 price_change from price a, price b, dim.time where b.time_id = a.time_id + 48 and b.time_id = time.time_id limit 10;&lt;br /&gt;+------------+----+--------+--------+--------------+&lt;br /&gt;| date       | tp | price  | price  | price_change |&lt;br /&gt;+------------+----+--------+--------+--------------+&lt;br /&gt;| 2000-01-02 |  1 | 979.49 | 923.78 |        94.00 |&lt;br /&gt;| 2000-01-02 |  2 | 509.22 |  10.22 |         2.00 |&lt;br /&gt;| 2000-01-02 |  3 | 607.63 | 279.77 |        46.00 |&lt;br /&gt;| 2000-01-02 |  4 | 510.47 | 368.20 |        72.00 |&lt;br /&gt;| 2000-01-02 |  5 | 729.49 |   1.70 |         0.00 |&lt;br /&gt;| 2000-01-02 |  6 | 116.03 | 903.87 |       779.00 |&lt;br /&gt;| 2000-01-02 |  7 | 391.66 | 514.25 |       131.00 |&lt;br /&gt;| 2000-01-02 |  8 | 610.23 | 859.65 |       141.00 |&lt;br /&gt;| 2000-01-02 |  9 | 876.16 | 755.51 |        86.00 |&lt;br /&gt;| 2000-01-02 | 10 | 550.10 | 198.58 |        36.00 |&lt;br /&gt;+------------+----+--------+--------+--------------+&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;As well as easy to query, it can make your data tables smaller by only having a integer of the right length  to specify the time.&lt;/div&gt;
&lt;/div&gt;

        
      </description>
    </item>
    
    <item>
      <title>OWA Mail Checker, keep alive</title>
      <link>https://tekao.net/posts/oww_mail_checker/</link>
      <pubDate>Wed, 25 Jan 2012 17:45:28 -0800</pubDate>
      
      <guid>https://tekao.net/posts/oww_mail_checker/</guid>
      <description>
        
          


&lt;p&gt;This Greasemonkey script keeps your Outlook 2010 OWA session alive by refreshing every 60s. It&#39;s basically an update of &lt;a href=&#34;http://blog.hacker.dk/2007/10/greasemonkey-owa-mailcheck-keepalive/&#34;&gt;http://blog.hacker.dk/2007/10/greasemonkey-owa-mailcheck-keepalive/&lt;/a&gt; to work with Outlook 2010. Make sure you use the light version of the app when logging in.&lt;/p&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;It will also show the number of unread emails in the title:&lt;/div&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a style=&#34;margin-left: 1em; margin-right: 1em;&#34; href=&#34;https://tekao.net/images/Screenshot-1.png&#34;&gt;&lt;img src=&#34;https://tekao.net/images/Screenshot-1.png&#34; alt=&#34;&#34; border=&#34;0&#34; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;Download here:&lt;br /&gt;&lt;a href=&#34;http://userscripts.org/scripts/source/124122.user.js&#34;&gt;http://userscripts.org/scripts/source/124122.user.js&lt;/a&gt;&lt;/div&gt;

        
      </description>
    </item>
    
  </channel>
</rss>
