<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ryan Armstrong's Blog</title>
    <description>The poorly maintained ramblings of an Infrastructure/Software engineer.
</description>
    <link>http://cavaliercoder.github.io/</link>
    <atom:link href="http://cavaliercoder.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 22 Aug 2018 20:36:12 +0000</pubDate>
    <lastBuildDate>Wed, 22 Aug 2018 20:36:12 +0000</lastBuildDate>
    <generator>Jekyll v3.7.3</generator>
    
      <item>
        <title>Dynamic stats in Go's expvar</title>
        <description>&lt;p&gt;Go’s expvar package enables a standardized way to publish runtime statistics
for your application. Out of the box, by importing &lt;code class=&quot;highlighter-rouge&quot;&gt;expvar&lt;/code&gt;, you will have a
HTTP handler registered at “/debug/vars” that will print a whole mess of GC,
memory and commandline stats as a JSON document.&lt;/p&gt;

&lt;p&gt;Expvar includes many primitives for safely setting or incrementing various
static metric types. This works great under normal circumstances, but there are
occasions when you might need to dynamically compute the desired metric value on
demand.&lt;/p&gt;

&lt;p&gt;A key example is “uptime”. How long has my service been up? Using the
traditional static approach, you would need to update this value periodically
(wasteful) and you risk retrieving an outdated value on request.&lt;/p&gt;

&lt;p&gt;The following code is the simplest way to export an “uptime” variable that is
computed on demand, using &lt;code class=&quot;highlighter-rouge&quot;&gt;expvar.Func&lt;/code&gt; and a closure function.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;expvar&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;net/http&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expvar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Publish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;uptime&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expvar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Since&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&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;Seconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;:8080&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultServeMux&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you had more complex needs - say, additional fields to consider - you could
instead implement the &lt;code class=&quot;highlighter-rouge&quot;&gt;expvar.Var&lt;/code&gt; interface&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;expvar&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;net/http&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;strconv&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uptimeVar&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// other important fields...&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uptimeVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// other things to compute...&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Since&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strconv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FormatFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'g'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uptimeVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expvar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Publish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;uptime&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;:8080&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultServeMux&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A possible tradeoff in using dynamic metrics is that you risk introducing
latency to your monitoring tools or health checks if your dynamic metrics are
too compute-heavy or if they depend on I/O.&lt;/p&gt;
</description>
        <pubDate>Mon, 11 Jun 2018 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/dynamic-stats-in-gos-expvar.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/dynamic-stats-in-gos-expvar.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Optimized abs() for int64 in Go</title>
        <description>&lt;p&gt;The Go programming language has no built in &lt;code class=&quot;highlighter-rouge&quot;&gt;abs&lt;/code&gt; function for computing the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Absolute_value&quot;&gt;absolute value of an integer&lt;/a&gt;.
That is, the non-negative representation of a negative or positive number.&lt;/p&gt;

&lt;p&gt;I recently needed an implementation of the &lt;code class=&quot;highlighter-rouge&quot;&gt;abs&lt;/code&gt; function to solve the
&lt;a href=&quot;http://adventofcode.com/2017/day/20&quot;&gt;Day 20&lt;/a&gt; challenge in
&lt;a href=&quot;http://adventofcode.com/2017/about&quot;&gt;Advent of Code 2017&lt;/a&gt;. If you want to learn
something new and get a kick out of testing yourself, I strongly recommend you
check it out!&lt;/p&gt;

&lt;p&gt;Go does include an &lt;code class=&quot;highlighter-rouge&quot;&gt;abs&lt;/code&gt; function in the &lt;code class=&quot;highlighter-rouge&quot;&gt;math&lt;/code&gt; package:
&lt;a href=&quot;https://golang.org/pkg/math/#Abs&quot;&gt;math.Abs&lt;/a&gt;. Unfortunately, this doesn’t fit my
use case, as it only accepts &lt;code class=&quot;highlighter-rouge&quot;&gt;float64&lt;/code&gt; as input and output. I need &lt;code class=&quot;highlighter-rouge&quot;&gt;int64&lt;/code&gt;. It
is possible to use &lt;code class=&quot;highlighter-rouge&quot;&gt;math.Abs&lt;/code&gt; via type conversion, but this introduces some
overhead for the conversion to &lt;code class=&quot;highlighter-rouge&quot;&gt;float64&lt;/code&gt; and back to &lt;code class=&quot;highlighter-rouge&quot;&gt;int64&lt;/code&gt;, as well as
introducing truncation for larger numbers.&lt;/p&gt;

&lt;p&gt;There is a &lt;a href=&quot;https://groups.google.com/forum/#!topic/golang-dev/nP5mWvwAXZo&quot;&gt;great discussion&lt;/a&gt; on optimizing
&lt;code class=&quot;highlighter-rouge&quot;&gt;math.Abs&lt;/code&gt; for floating point integers, but unfortunately these optimizations
don’t directly apply to integers, due to their underlying encoding.&lt;/p&gt;

&lt;p&gt;There must be other options. So begins my adventure into optimizing a simple
function in Go!&lt;/p&gt;

&lt;p&gt;You can find the source code and tests for this article on
&lt;a href=&quot;https://github.com/cavaliercoder/go-abs&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;type-conversion-vs-branching&quot;&gt;Type conversion vs. branching&lt;/h2&gt;

&lt;p&gt;The most obvious solution to me for this problem was a very basic function which
returns &lt;code class=&quot;highlighter-rouge&quot;&gt;n&lt;/code&gt; if &lt;code class=&quot;highlighter-rouge&quot;&gt;n&lt;/code&gt; is zero or greater, and returns negative &lt;code class=&quot;highlighter-rouge&quot;&gt;n&lt;/code&gt; if &lt;code class=&quot;highlighter-rouge&quot;&gt;n&lt;/code&gt; is less
than zero. The double negative is, of course, always positive.&lt;/p&gt;

&lt;p&gt;Since it relies on a branching control structure to calculate an absolute value,
let’s call this function &lt;code class=&quot;highlighter-rouge&quot;&gt;abs.WithBranch&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithBranch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It works! Curiously, this is currently (Go v1.9.x) how &lt;code class=&quot;highlighter-rouge&quot;&gt;math.Abs&lt;/code&gt; is
&lt;a href=&quot;https://github.com/golang/go/blob/release-branch.go1.9/src/math/abs.go&quot;&gt;implemented&lt;/a&gt;
for &lt;code class=&quot;highlighter-rouge&quot;&gt;float64&lt;/code&gt; numbers.&lt;/p&gt;

&lt;p&gt;Great! Branching works. But, does it improve on a call to &lt;code class=&quot;highlighter-rouge&quot;&gt;math.Abs&lt;/code&gt; using type
conversions? Let’s implement that too.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithStdLib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we are converting &lt;code class=&quot;highlighter-rouge&quot;&gt;n&lt;/code&gt; to a &lt;code class=&quot;highlighter-rouge&quot;&gt;float64&lt;/code&gt;, passing it to &lt;code class=&quot;highlighter-rouge&quot;&gt;math.Abs&lt;/code&gt; and then
converting the result from &lt;code class=&quot;highlighter-rouge&quot;&gt;float64&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;int64&lt;/code&gt;. Surely, this must introduce
some performance overhead?&lt;/p&gt;

&lt;p&gt;A couple of simple &lt;a href=&quot;https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go&quot;&gt;benchmark tests&lt;/a&gt; yield clear
results:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;$ go test -bench=.
goos: darwin
goarch: amd64
pkg: github.com/cavaliercoder/abs
BenchmarkWithBranch-8           2000000000               0.30 ns/op
BenchmarkWithStdLib-8           2000000000               0.79 ns/op
PASS
ok      github.com/cavaliercoder/abs    2.320s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At 0.30 ns/op (nanoseconds per operation), &lt;code class=&quot;highlighter-rouge&quot;&gt;WithBranch&lt;/code&gt; is more than twice as
fast! But our benchmark doesn’t represent the real world…&lt;/p&gt;

&lt;h2 id=&quot;benchmarking&quot;&gt;Benchmarking&lt;/h2&gt;

&lt;p&gt;We know that branching code breaks the sequential flow of a
program, meaning that &lt;a href=&quot;http://euler.mat.uson.mx/~havillam/ca/CS323/0708.cs-323007.html&quot;&gt;pipelining processors must predict what will happen
next&lt;/a&gt; to
maintain similar performance to a sequential program.&lt;/p&gt;

&lt;p&gt;In the above benchmark, we used a constant value as the input to each call to
&lt;code class=&quot;highlighter-rouge&quot;&gt;abs&lt;/code&gt;. Because of this, the processor is able to predict which branch to take
every time, because it’s the same every time.&lt;/p&gt;

&lt;p&gt;We can trip the processor’s ability to predict the flow of execution, and better
represent real world use-cases by introducing a Pseudo Random-Number Generator.
If the input is random, the output will be random and the processor’s
predictions should be wrong more often.&lt;/p&gt;

&lt;p&gt;Given there are only two possible futures, I assume a pipelining processor can
compute both outcomes at once, without a performance penalty. Let’s test by
experiment.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ go test -bench=.
goos: darwin
goarch: amd64
pkg: github.com/cavaliercoder/go-abs
BenchmarkRand-8                 1000000000               2.59 ns/op
BenchmarkWithBranch-8           200000000                6.36 ns/op
BenchmarkWithStdLib-8           500000000                3.13 ns/op
PASS
ok      github.com/cavaliercoder/go-abs 6.692s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve introduced a new benchmark, &lt;code class=&quot;highlighter-rouge&quot;&gt;BenchmarkRand&lt;/code&gt;, to measure the overhead of the
RNG. We can substract the resulting overhead from the other benchmarks for an
approximation of their actual runtime.&lt;/p&gt;

&lt;p&gt;It turns out, &lt;code class=&quot;highlighter-rouge&quot;&gt;abs.WithBranch&lt;/code&gt; doesn’t perform very well with random input. In
fact, &lt;code class=&quot;highlighter-rouge&quot;&gt;abs.WithStdLib&lt;/code&gt; is nearly seven times faster.&lt;/p&gt;

&lt;h2 id=&quot;truncation&quot;&gt;Truncation&lt;/h2&gt;

&lt;p&gt;Before we throw out the branching approach, it should be noted that it has the
advantage that it won’t truncate any large numbers by converting from a signed
&lt;code class=&quot;highlighter-rouge&quot;&gt;int64&lt;/code&gt; to an IEEE-754 &lt;code class=&quot;highlighter-rouge&quot;&gt;float64&lt;/code&gt;, losing bits to represent the decimal point.&lt;/p&gt;

&lt;p&gt;By example, &lt;code class=&quot;highlighter-rouge&quot;&gt;abs.WithBranch(-9223372036854775807)&lt;/code&gt; correctly returns
&lt;code class=&quot;highlighter-rouge&quot;&gt;9223372036854775807&lt;/code&gt;, while &lt;code class=&quot;highlighter-rouge&quot;&gt;abs.WithStdLib(-9223372036854775807)&lt;/code&gt; experiences
an overflow during the type conversion and incorrectly returns
&lt;code class=&quot;highlighter-rouge&quot;&gt;-9223372036854775808&lt;/code&gt;. It even returns the same incorrect, negative result for
a high positive input, &lt;code class=&quot;highlighter-rouge&quot;&gt;abs.WithStdLib(9223372036854775807)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The type conversion approach is faster with random input, but the branching
approach is more correct. Can we do better?&lt;/p&gt;

&lt;h2 id=&quot;a-non-branching-solution&quot;&gt;A non-branching solution&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://books.google.com.au/books?id=VicPJYM0I5QC&amp;amp;lpg=PA18&amp;amp;ots=2o-SROAuXq&amp;amp;dq=hackers%20delight%20absolute&amp;amp;pg=PA18#v=onepage&amp;amp;q=hackers%20delight%20absolute&amp;amp;f=false&quot;&gt;Chapter 2 of Hacker’s Delight by Henry S. Warren&lt;/a&gt;
introduces us to a branch-free way of computing the absolute value of a signed
integer using a little &lt;a href=&quot;https://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html&quot;&gt;Two’s Complement&lt;/a&gt; arithmetic.&lt;/p&gt;

&lt;p&gt;To compute the absolute value of &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;, first, we compute the value &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt; which is
equal to &lt;code class=&quot;highlighter-rouge&quot;&gt;x ⟫ 63&lt;/code&gt;. That is, &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; right shifted by 63 bits on a 64 bit
architecture. Finally, compute &lt;code class=&quot;highlighter-rouge&quot;&gt;(x ⨁ y) - y&lt;/code&gt;. That is, &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; exclusive-or &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt;,
take &lt;code class=&quot;highlighter-rouge&quot;&gt;y&lt;/code&gt;. This yields the absolute value of &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because we live hardcore, let’s implement this in assembly!&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// abs.go&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithASM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;// abs_amd64.s
TEXT ·WithASM(SB),$0
  MOVQ    n+0(FP), AX     // copy input to AX
  MOVQ    AX, CX          // y ← x
  SARQ    $63, CX         // y ← y ⟫ 63
  XORQ    CX, AX          // x ← x ⨁ y
  SUBQ    CX, AX          // x ← x - y
  MOVQ    AX, ret+8(FP)   // copy result to return value
  RET
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We start above with a Go function declaration for &lt;code class=&quot;highlighter-rouge&quot;&gt;WithASM&lt;/code&gt; that has no
immediate implementation. In a separate ASM code file, we declare the body in
&lt;a href=&quot;https://golang.org/doc/asm&quot;&gt;Go’s Assembler&lt;/a&gt; language. Note that this
implementation is only valid on &lt;code class=&quot;highlighter-rouge&quot;&gt;amd64&lt;/code&gt; architecture systems, so we advise the
compiler by giving the file name the &lt;code class=&quot;highlighter-rouge&quot;&gt;_amd64.s&lt;/code&gt; suffix.&lt;/p&gt;

&lt;p&gt;And the benchmarks results:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;$ go test -bench=.
goos: darwin
goarch: amd64
pkg: github.com/cavaliercoder/go-abs
BenchmarkRand-8                 1000000000               2.52 ns/op
BenchmarkWithBranch-8           200000000                5.91 ns/op
BenchmarkWithStdLib-8           500000000                2.88 ns/op
BenchmarkWithASM-8              1000000000               2.78 ns/op
PASS
ok      github.com/cavaliercoder/go-abs 9.454s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All right! We observe a minor improvement of 0.10 ns/op. That’s actually a ~38%
speed up. Still, there could be more! It might help to understand how Go
treats ASM code.&lt;/p&gt;

&lt;h2 id=&quot;compiler-optimizations&quot;&gt;Compiler optimizations&lt;/h2&gt;

&lt;p&gt;We need visibility of the optimizations the compiler is applying to the Go
functions. The compiler accepts the &lt;code class=&quot;highlighter-rouge&quot;&gt;-m&lt;/code&gt; flag to “print optimization decisions”.
This can also be enabled in &lt;code class=&quot;highlighter-rouge&quot;&gt;go build&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;go test&lt;/code&gt; with the &lt;code class=&quot;highlighter-rouge&quot;&gt;-gcflags=-m&lt;/code&gt;
argument.&lt;/p&gt;

&lt;p&gt;Here’s what we see:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;$ go tool compile -m abs.go
# github.com/cavaliercoder/abs
./abs.go:11:6: can inline WithBranch
./abs.go:21:6: can inline WithStdLib
./abs.go:22:23: inlining call to math.Abs
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For simple functions like ours, the Go compiler supports &lt;a href=&quot;https://github.com/golang/go/wiki/CompilerOptimizations#function-inlining&quot;&gt;function inlining&lt;/a&gt;.
Function inlining means that calls to our function are replaced inline with the
actual body of our function.&lt;/p&gt;

&lt;p&gt;By example,&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;github.com/cavaliercoder/abs&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithBranch&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;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;… might actually be compiled more similar to:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;According to the compiler output above, &lt;code class=&quot;highlighter-rouge&quot;&gt;WithBranch&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;WithStdLib&lt;/code&gt; are able
to be inlined, but &lt;code class=&quot;highlighter-rouge&quot;&gt;WithASM&lt;/code&gt; is not. Even the underlying call to &lt;code class=&quot;highlighter-rouge&quot;&gt;math.Abs&lt;/code&gt; is
inlined into &lt;code class=&quot;highlighter-rouge&quot;&gt;WithStdLib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since our &lt;code class=&quot;highlighter-rouge&quot;&gt;WithASM&lt;/code&gt; function cannot be inlined, each caller incurs the overhead
of a function call. This means writing a stack frame, copying in arguments,
branching the program pointer, etc.&lt;/p&gt;

&lt;p&gt;What if we even the playing field and disable inlining on our other Go
functions? We can do this with a simple pragma comment before the function
declaration: &lt;code class=&quot;highlighter-rouge&quot;&gt;//go:noinline&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;//go:noinline&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithBranch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running the compiler again, we see many fewer optimizations:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;$ go tool compile -m abs.go
abs.go:22:23: inlining call to math.Abs
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here are the benchmark results:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$go test -bench=.
goos: darwin
goarch: amd64
pkg: github.com/cavaliercoder/go-abs
BenchmarkRand-8                 1000000000               2.40 ns/op
BenchmarkWithBranch-8           200000000                7.06 ns/op
BenchmarkWithStdLib-8           500000000                3.46 ns/op
BenchmarkWithASM-8              1000000000               2.86 ns/op
PASS
ok      github.com/cavaliercoder/go-abs 10.032s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The performance of &lt;code class=&quot;highlighter-rouge&quot;&gt;WithASM&lt;/code&gt; remains unchanged, while &lt;code class=&quot;highlighter-rouge&quot;&gt;WithStdLib&lt;/code&gt; is three
times slower thanks to the overhead of the non-inline function call.&lt;/p&gt;

&lt;p&gt;The lesson learned for me here, is that the performance gained by implementing
ASM by hand needs to outweigh the benefits of compiler type safety, garbage
collection, function inlining, etc. In most cases, this won’t be the true,
though there are exceptions; like taking advantage of &lt;a href=&quot;https://goroutines.com/asm&quot;&gt;SIMD&lt;/a&gt; instructions for cryptography or media encoding.&lt;/p&gt;

&lt;p&gt;Running the benchmarks a few times it becomes clear that there is a reasonable
performance gain to be had via ASM. But, can we somehow retain the benefits of
inlining?&lt;/p&gt;

&lt;h2 id=&quot;one-inline-function-please&quot;&gt;One inline function, please&lt;/h2&gt;

&lt;p&gt;The Go compiler cannot inline functions that are implemented in assembler, but
implementing our Two’s Complement arithmetic in Go is easy:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithTwosComplement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;63&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// y ← x ⟫ 63&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// (x ⨁ y) - y&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The compiler says our new function can be inlined:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;$ go tool compile -m abs.go
...
abs.go:26:6: can inline WithTwosComplement
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;How does it perform? It turns out, when we re-enable inlining, it performs even
better! A whole 0.18 ns better, or roughly 60%.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;$ go test -bench=.
goos: darwin
goarch: amd64
pkg: github.com/cavaliercoder/go-abs
BenchmarkRand-8                         1000000000               2.39 ns/op
BenchmarkWithBranch-8                   300000000                5.71 ns/op
BenchmarkWithStdLib-8                   1000000000               3.01 ns/op
BenchmarkWithASM-8                      1000000000               2.88 ns/op
BenchmarkWithTwosComplement-8           1000000000               2.70 ns/op
PASS
ok      github.com/cavaliercoder/go-abs 14.417s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that the overhead of a function call is gone, the Two’s Complement
implementation in Go out performs the ASM implementation. Curiosity might lead
us next to wonder how similar the compiler’s output is to our hand-crafted ASM?&lt;/p&gt;

&lt;p&gt;There’s an app for that.&lt;/p&gt;

&lt;p&gt;Passing &lt;code class=&quot;highlighter-rouge&quot;&gt;-S&lt;/code&gt; to the Go compiler causes it to “print assembly listing”.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;$ go tool compile -S abs.go
...
&quot;&quot;.WithTwosComplement STEXT nosplit size=24 args=0x10 locals=0x0
        0x0000 00000 (abs.go:26)        TEXT    &quot;&quot;.WithTwosComplement(SB), NOSPLIT, $0-16
        0x0000 00000 (abs.go:26)        FUNCDATA        $0, gclocals·f207267fbf96a0178e8758c6e3e0ce28(SB)
        0x0000 00000 (abs.go:26)        FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (abs.go:26)        MOVQ    &quot;&quot;.n+8(SP), AX
        0x0005 00005 (abs.go:26)        MOVQ    AX, CX
        0x0008 00008 (abs.go:27)        SARQ    $63, AX
        0x000c 00012 (abs.go:28)        XORQ    AX, CX
        0x000f 00015 (abs.go:28)        SUBQ    AX, CX
        0x0012 00018 (abs.go:28)        MOVQ    CX, &quot;&quot;.~r1+16(SP)
        0x0017 00023 (abs.go:28)        RET
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The compiler’s implementation is exactly the god-damned same. Only this time, it
has the advantage of being correctly configured and being cross-platform
portable! Running the compiler again with &lt;code class=&quot;highlighter-rouge&quot;&gt;GOARCH=386&lt;/code&gt; produces a more
complicated program that handles the 64-bit arithmetic on 32-bit machines.&lt;/p&gt;

&lt;p&gt;I need to be more trusting and slightly less hardcore.&lt;/p&gt;

&lt;p&gt;One last note about memory allocations; all of our implementations exhibit the
same ideal behavior. When I run &lt;code class=&quot;highlighter-rouge&quot;&gt;go test -bench=. -benchmem&lt;/code&gt;, I observe that
each of the functions result in zero allocation operations and zero bytes
allocated within the function body.&lt;/p&gt;

&lt;h2 id=&quot;homework&quot;&gt;Homework&lt;/h2&gt;

&lt;p&gt;A few exercises for the reader:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;What if we implemented the assembly for &lt;code class=&quot;highlighter-rouge&quot;&gt;abs&lt;/code&gt; as a compiler intrinsic, rather
than a runtime function call?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;What if we used the &lt;code class=&quot;highlighter-rouge&quot;&gt;PABSQ&lt;/code&gt; instruction included in SSSE3? This is not
currently supported in Go’s ASM.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;The Two’s Complement implementation in Go offers portability, function inlining,
predictable performance, zero allocations and no integer truncation due to type
conversions. The benchmarks showed a significant speed advantage over the other
approaches.&lt;/p&gt;

&lt;p&gt;The end result:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;63&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 13 Jan 2018 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/optimized-abs-for-int64-in-go.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/optimized-abs-for-int64-in-go.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Finding the latest CentOS AMI</title>
        <description>&lt;p&gt;Looking for the latest CentOS AMI image in your region? I’m frequently rewriting
the following code snippets, so thought I would capture them here for posterity.&lt;/p&gt;

&lt;p&gt;Each snippet makes the following assumptions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You want an HVM, EBS backed, x86_64 image of the latest version of CentOS 7&lt;/li&gt;
  &lt;li&gt;You have configured the desired region and credentials for the AWS SDK&lt;/li&gt;
  &lt;li&gt;The Owner ID for the official CentOS organisation remains &lt;code class=&quot;highlighter-rouge&quot;&gt;679593333241&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;bash--aws-cli&quot;&gt;Bash / AWS CLI&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws ec2 describe-images &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--owners&lt;/span&gt; 679593333241 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--filters&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;name,Values&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'CentOS Linux 7 x86_64 HVM EBS*'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;architecture,Values&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;x86_64 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;root-device-type,Values&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ebs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'sort_by(Images, &amp;amp;Name)[-1].ImageId'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;python--boto3&quot;&gt;Python / Boto3&lt;/h3&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;json&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;boto3&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;EC2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'ec2'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EC2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;describe_images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Owners&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'679593333241'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# CentOS&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Filters&lt;/span&gt;&lt;span class=&quot;o&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;s&quot;&gt;'Name'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'name'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Values'&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;s&quot;&gt;'CentOS Linux 7 x86_64 HVM EBS *'&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;s&quot;&gt;'Name'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'architecture'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Values'&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;s&quot;&gt;'x86_64'&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;s&quot;&gt;'Name'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'root-device-type'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Values'&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;s&quot;&gt;'ebs'&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;n&quot;&gt;amis&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Images'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'CreationDate'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;o&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;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'ImageId'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;terraform&quot;&gt;Terraform&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-hcl&quot;&gt;data &quot;aws_ami&quot; &quot;centos&quot; {
  owners      = [&quot;679593333241&quot;]
  most_recent = true

  filter {
    name   = &quot;name&quot;
    values = [&quot;CentOS Linux 7 x86_64 HVM EBS *&quot;]
  }

  filter {
    name   = &quot;architecture&quot;
    values = [&quot;x86_64&quot;]
  }

  filter {
    name   = &quot;root-device-type&quot;
    values = [&quot;ebs&quot;]
  }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
        <pubDate>Thu, 02 Nov 2017 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/finding-the-latest-centos-ami.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/finding-the-latest-centos-ami.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Inline vs. discrete rules for AWS Security Groups in Terraform</title>
        <description>&lt;p&gt;There are two ways to configure AWS Security Groups in Terraform. You may define
rules &lt;em&gt;inline&lt;/em&gt; with a &lt;code class=&quot;highlighter-rouge&quot;&gt;aws_security_group&lt;/code&gt; resource or you may define additional
discrete &lt;code class=&quot;highlighter-rouge&quot;&gt;aws_security_group_rule&lt;/code&gt; resources.&lt;/p&gt;

&lt;p&gt;My first instinct was to define a “base” Security Group using inline rules and
then extend on it using external rules. Bad idea. More on that later.&lt;/p&gt;

&lt;p&gt;For the two valid options though, there are important implications and I found
these were not clear at the time of writing (circa Terraform v0.9.11). After a
little research and experimentation I have a much clearer understanding and hope
to save you all the bother.&lt;/p&gt;

&lt;p&gt;This article focuses on managing AWS Security Groups in Terraform but you will
find that all of the principals explored here apply equally to Network ACLs and
Route Tables - both of which allow inline or external rule management.&lt;/p&gt;

&lt;h2 id=&quot;two-approaches&quot;&gt;Two approaches&lt;/h2&gt;

&lt;p&gt;Here’s how an &lt;em&gt;inline&lt;/em&gt; Security Group definition looks:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-hcl&quot;&gt;resource &quot;aws_security_group&quot; &quot;allow_all&quot; {
  name        = &quot;allow_all&quot;
  description = &quot;Allow all inbound traffic&quot;

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = &quot;-1&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = &quot;-1&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Above there are two rules, an &lt;code class=&quot;highlighter-rouge&quot;&gt;ingress&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;egress&lt;/code&gt; rule defined inside or
&lt;em&gt;inline&lt;/em&gt; with the &lt;code class=&quot;highlighter-rouge&quot;&gt;aws_security_group&lt;/code&gt; resource block.&lt;/p&gt;

&lt;p&gt;Here’s how the same idea can be expressed using external rules via the
&lt;code class=&quot;highlighter-rouge&quot;&gt;aws_security_group_rule&lt;/code&gt; resource:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-hcl&quot;&gt;resource &quot;aws_security_group&quot; &quot;allow_all&quot; {
  name        = &quot;allow_all&quot;
  description = &quot;Allow all inbound traffic&quot;
}

resource &quot;aws_security_group_rule&quot; &quot;ingress&quot; {
  type        = &quot;ingress&quot;
  from_port   = 0
  to_port     = 0
  protocol    = -1
  cidr_blocks = [&quot;0.0.0.0/0&quot;]

  security_group_id = &quot;${aws_security_group.allow_all.id}&quot;
}

resource &quot;aws_security_group_rule&quot; &quot;egress&quot; {
  type        = &quot;egress&quot;
  from_port   = 0
  to_port     = 0
  protocol    = -1
  cidr_blocks = [&quot;0.0.0.0/0&quot;]

  security_group_id = &quot;${aws_security_group.allow_all.id}&quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The Security Group and each of its rules are defined as discrete resources,
intimately linked together in loving union by the &lt;code class=&quot;highlighter-rouge&quot;&gt;security_group_id&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;A reasonable person might posit that the outcome of both configurations would
be the same, but they are different in subtle ways - ways that might hurt a bit
if not clearly understood.&lt;/p&gt;

&lt;h2 id=&quot;option-1-external-rules&quot;&gt;Option 1: External rules&lt;/h2&gt;

&lt;p&gt;I had hoped that external rules would function similar to Puppet’s &lt;code class=&quot;highlighter-rouge&quot;&gt;concat&lt;/code&gt;
module - gathering partial resources defined anywhere in the graph and then
enforcing the sum state. The reality however, which does make sense, is that the
desired state is managed non-destructively.&lt;/p&gt;

&lt;p&gt;You can test this, by manually adding a rule to the Security Group created by
the Terraform code above (the snippet with only external rules). If you run
&lt;code class=&quot;highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt;, it will ignore this manually created rule.&lt;/p&gt;

&lt;p&gt;What this means, is that you can add and enforce rules on a Security Group that
was created elsewhere. Just be cautious of conflicts with existing rules,
precedence and collisions in your rule numbers.&lt;/p&gt;

&lt;h3 id=&quot;pros&quot;&gt;Pros&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Rules can be added non-destructively to unmanaged Security Groups. This might
be useful for Security Groups associated with shared services, such as a
Bastion host. Terraform configurations from all over the kingdom can create
rules on a common Security Group to enable the access they require.&lt;/p&gt;

    &lt;p&gt;Ideally, one would create and associate distinct, well-tagged Security Groups
for each use-case but we are limited to only &lt;a href=&quot;http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Appendix_Limits.html#vpc-limits-security-groups&quot;&gt;five Security Groups per network
interface&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Security Groups can be “modularized” and extended upon. You might write a
Terraform module that creates a Security Group with standardized naming,
tagging, lifecycle and maybe even some base access rules. Additional rules
could then be added to the Security Group returned by the module using
&lt;code class=&quot;highlighter-rouge&quot;&gt;aws_security_group_rule&lt;/code&gt; resources.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;cons&quot;&gt;Cons&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Security Group rules that are added by accident or by nefarious means will not
be nuked by Terraform. It won’t even report that they exist.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Infrastructure code should succinctly describe and quasi-document your actual
infrastructure. Defining rules for a single Security Group in disparate files
in your code-base makes it difficult to see at a glance what the state of a
Security group should be.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It’s difficult to know or manage for how externally created rules will
effect the rules you are configuring in Terraform. This is particularly
important for Network ACLs, where a rule defined elsewhere could have higher
precedence than yours and might explicitly deny a port you intend to open.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;option-2-inline-rules&quot;&gt;Option 2: Inline rules&lt;/h2&gt;

&lt;p&gt;When rules are defined inline, a Security Group is managed destructively. That
is, any rule not defined inline, including rules defined elsewhere in Terraform
and rules added manually or via other tools, will be unapologetically destroyed
whenever Terraform next runs.&lt;/p&gt;

&lt;h3 id=&quot;pros-1&quot;&gt;Pros&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Security Groups are for security. They need to be clear, understood and well
configured. Using inline rules means your resource definition is complete,
definitive and deterministically provisioned. You can have confidence that
there are no rules being described elsewhere in the code-base or added outside
of Terraform. If there are, they get destroyed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;cons-1&quot;&gt;Cons&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;There’s no way to share code between inline-style Security Groups. This might
mean lots of repetition  for Security Groups that are &lt;em&gt;mostly&lt;/em&gt; the same;
having a common set of rules with minor exceptions between them. Make use of
the five available Security Group slots per network interface to allow re-use
of shared base Security Groups - though this is difficult if you’re using
least-privilege, point-to-point access rules.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You can’t use the &lt;code class=&quot;highlighter-rouge&quot;&gt;count&lt;/code&gt; meta-parameter to described rules in a loop.&lt;/p&gt;

    &lt;p&gt;It works with external rules:&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-hcl&quot;&gt;variable &quot;http_ports&quot; {
  default = [&quot;80&quot;, &quot;443&quot;, &quot;8080&quot;, &quot;8443&quot;]
}

resource &quot;aws_security_group_rule&quot; &quot;ingress_http&quot; {
  count = &quot;${length(var.http_ports)}&quot;

  type        = &quot;ingress&quot;
  protocol    = &quot;tcp&quot;
  cidr_blocks = [&quot;0.0.0.0/0&quot;]
  from_port   = &quot;${element(var.http_ports, count.index)}&quot;
  to_port     = &quot;${element(var.http_ports, count.index)}&quot;

  security_group_id = &quot;${aws_security_group.allow_all.id}&quot;
}
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;… but not with inline rules:&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-hcl&quot;&gt;variable &quot;http_ports&quot; {
  default = [&quot;80&quot;, &quot;443&quot;, &quot;8080&quot;, &quot;8443&quot;]
}

resource &quot;aws_security_group&quot; &quot;allow_all&quot; {
  name        = &quot;allow_all&quot;
  description = &quot;Allow all inbound traffic&quot;

  ingress {
    count = &quot;${length(var.http_ports)}&quot;

    protocol    = &quot;tcp&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
    from_port   = &quot;${element(var.http_ports, count.index)}&quot;
    to_port     = &quot;${element(var.http_ports, count.index)}&quot;
  }
}

# 1 error(s) occurred:
#
# * aws_security_group.allow_all: ingress.0: invalid or unknown key: count
#
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;You’re going to need to code up an inline rule for each port.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-not-both&quot;&gt;Why not both?&lt;/h2&gt;

&lt;p&gt;My naive first approach was to blend both approaches. I hoped to create a
configurable &lt;code class=&quot;highlighter-rouge&quot;&gt;aws_security_group&lt;/code&gt; “module” that contained some mandatory rules,
like allowing ingress SSH, monitoring, etc. This module could then be extended
with additional rules using &lt;code class=&quot;highlighter-rouge&quot;&gt;aws_security_group_rules&lt;/code&gt; resources. A kinda
pseudo-OOP-abstract-class approach.&lt;/p&gt;

&lt;p&gt;Fortunately, the &lt;a href=&quot;https://www.terraform.io/docs/providers/aws/r/security_group.html&quot;&gt;Terraform documentation&lt;/a&gt;
contained a well-lit warning sign:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;At this time you cannot use a Security Group with in-line rules in conjunction
with any Security Group Rule resources. Doing so will cause a conflict of rule
settings and will overwrite rules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What happens when you combine both methods? Here’s a cool high-school science
lab experiment for you!&lt;/p&gt;

&lt;p&gt;The following Terraform code defines both inline rules, and an external
&lt;code class=&quot;highlighter-rouge&quot;&gt;ingress_http&lt;/code&gt; rule.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-hcl&quot;&gt;resource &quot;aws_security_group&quot; &quot;allow_all&quot; {
  name        = &quot;allow_all&quot;
  description = &quot;Allow all inbound traffic&quot;

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = &quot;-1&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = &quot;-1&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }
}

resource &quot;aws_security_group_rule&quot; &quot;ingress_http&quot; {
  type        = &quot;ingress&quot;
  from_port   = 80
  to_port     = 80
  protocol    = 6
  cidr_blocks = [&quot;0.0.0.0/0&quot;]

  security_group_id = &quot;${aws_security_group.allow_all.id}&quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;instructions&quot;&gt;Instructions&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Apply this code with &lt;code class=&quot;highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt; - it should create the &lt;code class=&quot;highlighter-rouge&quot;&gt;ingress_http&lt;/code&gt;
rule&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Apply the same code again with &lt;code class=&quot;highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt; - it should remove the newly
created &lt;code class=&quot;highlighter-rouge&quot;&gt;ingress_http&lt;/code&gt; rule&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Go to step 1&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Terraform will create and then destroy the external rule on each alternating
invocation - like that scene with Dormammu in Marvel’s Dr Strange.&lt;/p&gt;

&lt;p&gt;Bug? No. It actually kinda makes sense. Jake Champlin from HashiCorp explains it
on a &lt;a href=&quot;https://github.com/hashicorp/terraform/issues/11011#issuecomment-283076580&quot;&gt;related GitHub issue&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;I’ve come to prefer using inline rules where possible. It means our Security
Groups match the code and phantom ‘allow all’ rules can’t be introduced that
would break our security model and the integrity of our tests.&lt;/p&gt;

&lt;p&gt;It does mean we have to duplicate some rules in a few Security Groups, and keep
these definitions in sync, but I share the opinion that &lt;a href=&quot;https://www.youtube.com/watch?v=PAAkCSZUG1c&amp;amp;t=9m28s&quot;&gt;a little copying is
better than a little dependency&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more information about AWS Security Groups in Terraform, please see:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_SecurityGroups.html&quot;&gt;AWS: Security Groups for your VPC&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.terraform.io/docs/providers/aws/r/security_group.html&quot;&gt;Terraform: aws_security_group&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.terraform.io/docs/providers/aws/r/security_group_rule.html&quot;&gt;Terraform: aws_security_group_rule&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 15 Jul 2017 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/inline-vs-discrete-security-groups-in-terraform.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/inline-vs-discrete-security-groups-in-terraform.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>A WebOps Postmortem</title>
        <description>&lt;p&gt;Early in the day, May 18, 2017, alerts started appearing from our Content
Distribution Network that a number of user requests were being served with
&lt;a href=&quot;https://tools.ietf.org/html/rfc7231#section-6.6.5&quot;&gt;504 Gateway Timeout&lt;/a&gt;
responses.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;inline right&quot; src=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/shitting-pants.jpg&quot; alt=&quot;Negan: I hope you have your shitting pants on&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I lead the Production Engineering team at Seven West Media in Western Australia
and the alerts indicated an issue with &lt;a href=&quot;https://thewest.com.au&quot;&gt;thewest.com.au&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our internal process did produce a postmortem report that differs from this one,
but only in formality, not in terms of the facts. This is my summarized and more
technically focused treatment of the incident.&lt;/p&gt;

&lt;p&gt;I hope to highlight here some of the principles that we employ that were
advantageous to us, as well as simply exploring an interesting problem that
reminded us to stay humble.&lt;/p&gt;

&lt;p&gt;While reports came in from our colleagues and customers that images were failing
to display on the site, a killer team of Developers and SysAdmins invoked our
Incident Response process. We borrow heavily from ITIL Incident Management and
&lt;a href=&quot;https://landing.google.com/sre/book.html&quot;&gt;Google’s Site Reliability Engineering&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The process calls for us to stay calm, objective and to seek out quality
information for better decision making.&lt;/p&gt;

&lt;h2 id=&quot;network-io-deviations&quot;&gt;Network I/O deviations&lt;/h2&gt;
&lt;p&gt;Initially we discovered unusual network I/O patterns on our Content API servers.&lt;/p&gt;

&lt;p&gt;The Content API is a Node.js/Express application fronted by a local instance of
nginx. It is responsible for serving article content, listings, curations, etc.
to both our internal web servers (for server-side rendering) and to our
client-side Javascript which uses the Single-Page Application model (see:
&lt;a href=&quot;https://medium.com/airbnb-engineering/isomorphic-javascript-the-future-of-web-apps-10882b7a2ebc&quot;&gt;Isomorphic Javascript: The Future of Web Apps&lt;/a&gt;).
It also serves static assets, such as images, which it pipes directly from
Amazon S3.&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;lightbox&quot; href=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/network-io.png&quot;&gt;
  &lt;img src=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/network-io.png&quot; alt=&quot;Network I/O on Content API instance&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above figure from Zabbix shows that network I/O became more &lt;em&gt;“ordered”&lt;/em&gt; on
one of the Content API instances around 7 AM. A &lt;a href=&quot;https://oneworld-publications.com/simply-complexity.html&quot;&gt;brief exploration of Complexity
Theory&lt;/a&gt; teaches us
that things go wrong when systems transition to exhibiting ordered behaviors.&lt;/p&gt;

&lt;p&gt;We employed &lt;a href=&quot;http://www.brendangregg.com/usemethod.html&quot;&gt;Brendan Gregg’s USE method&lt;/a&gt;
to build a clearer picture of network performance. We observed:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;throughput between each component was otherwise typical, with plenty of
headroom&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;no dropped packets, interface errors or other known TCP/IP issues that would
indicate saturation or congestion&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;no error messages in any of the counters or logs we knew to check&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;no network issues were being reported by AWS&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Health checks on the Content API servers were passing&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There was one important metric that we neglected to check for saturation but
more on that later!&lt;/p&gt;

&lt;h2 id=&quot;http-499-errors&quot;&gt;HTTP 499 errors&lt;/h2&gt;

&lt;p&gt;One fastidious engineer noticed something else amiss: a large number of
&lt;a href=&quot;http://lxr.nginx.org/source/src/http/ngx_http_request.h#0124&quot;&gt;499 Client Closed Request&lt;/a&gt;
responses being logged by nginx on the same Content API servers.&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;lightbox&quot; href=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/499-graph.png&quot;&gt;
  &lt;img src=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/499-graph.png&quot; alt=&quot;HTTP 409 status graph&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above screen grab from Sumo Logic of the same instance shows the uptick in
499 status messages at the same time that the network I/O anomaly appeared. The
uptick is obvious in this image, but it was visually masked on our dashboards
by the thousands of successful requests occurring at the same time.&lt;/p&gt;

&lt;p&gt;Reviewing the nginx logs on each server revealed an interesting pattern that
helped us to understand the scope of the issue: all 499 responses were
constrained to static assets only. Requests for dynamic content were unaffected.&lt;/p&gt;

&lt;p&gt;This finding was easy to confirm locally on any of the production servers. A
quick &lt;code class=&quot;highlighter-rouge&quot;&gt;$ curl -iv http://localhost/...&lt;/code&gt; for any static asset would
hang until the client cancelled the request. The same &lt;code class=&quot;highlighter-rouge&quot;&gt;curl&lt;/code&gt; request for any
other resource behaved as expected.&lt;/p&gt;

&lt;p&gt;This test also validated that no error was logged via nginx or the Node.js
application. The application logs did show an incoming request but no outbound
response or error.&lt;/p&gt;

&lt;p&gt;So here’s what we knew:&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;lightbox&quot; href=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/what-we-know.png&quot;&gt;
  &lt;img src=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/what-we-know.png&quot; alt=&quot;Sequence diagram&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Either our CDN or Load Balancing layer was cancelling some requests and
returning status 504 as no timely response was received from upstream&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Nginx was logging a status 499 when the downstream components cancelled a
request&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Requests to the Content API for static assets were falling into a black hole
which meant that image content on the site was failing to display&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point we decided we had sufficient information about the scope of the
issue to consider a mitigation plan.&lt;/p&gt;

&lt;h2 id=&quot;stem-the-bleeding&quot;&gt;Stem the bleeding&lt;/h2&gt;
&lt;p&gt;Our incident response process emphasizes small, non-destructive changes over
wholesale repairs where possible. I annoyingly repeat the mantra to my team that
we must:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Avoid corrective surgery - just stem the bleeding&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Corrective surgery is best performed once the situation has stabilized, we have
better information and everyone is cool, calm and collected.&lt;/p&gt;

&lt;p&gt;We formed a hypothesis that freshly launched instances of the Content API might
behave correctly and we found that we were right! The new servers were not
producing any errors once marked in.&lt;/p&gt;

&lt;p&gt;We did have the option to instead simply restart the unhealthy instances, but
our process also encourages the preservation of evidence. Restarting the
instances would have reset their state - and as we learned later - would have
reset the key metric we neglected to observe. We might not have found the root
cause until the issue surfaced again.&lt;/p&gt;

&lt;p&gt;Besides that, servers should be treated &lt;a href=&quot;http://cloudscaling.com/blog/cloud-computing/the-history-of-pets-vs-cattle/&quot;&gt;like cattle, not pets&lt;/a&gt;. A
freshly launched instance represents a known good state.&lt;/p&gt;

&lt;p&gt;With the healthy instances in rotation, everything stabilized. The 499 log
messages disappeared, our &lt;code class=&quot;highlighter-rouge&quot;&gt;curl&lt;/code&gt; test started behaving correctly and users
were confirming a positive outcome.&lt;/p&gt;

&lt;h2 id=&quot;root-cause-analysis&quot;&gt;Root cause analysis&lt;/h2&gt;

&lt;p&gt;With clearer heads and happy customers, we had the opportunity to take a
detailed look at the unhealthy servers and compare them to healthy servers.&lt;/p&gt;

&lt;p&gt;A call to &lt;code class=&quot;highlighter-rouge&quot;&gt;$ netstat -t4&lt;/code&gt; shows all TCPv4 sockets on a Linux system. The
following figure shows the results of this call on both an unhealthy (left) and
healthy (right) Content API instance.&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;lightbox&quot; href=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/netstat-bad.png&quot;&gt;
  &lt;img class=&quot;osx-window&quot; src=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/netstat-bad.png&quot; alt=&quot;netstat session&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the unhealthy instance has a large number of sockets in a
&lt;code class=&quot;highlighter-rouge&quot;&gt;CLOSE_WAIT&lt;/code&gt; state and also that most sockets have a large quantity of packets
in the Receive Queue (second column). All of these sockets list AWS S3 as the
foreign end point.&lt;/p&gt;

&lt;p&gt;By comparison the healthy server has only one &lt;code class=&quot;highlighter-rouge&quot;&gt;CLOSE_WAIT&lt;/code&gt; socket and the
Receive Queues are mostly empty, except for the lonely &lt;code class=&quot;highlighter-rouge&quot;&gt;CLOSE_WAIT&lt;/code&gt; socket.
Could this be the beginning of a very slow connection leak?&lt;/p&gt;

&lt;p&gt;Remember that key metric that we neglected to observe during our network
performance observations? We needed to measure the &lt;strong&gt;number of &lt;code class=&quot;highlighter-rouge&quot;&gt;CLOSE_WAIT&lt;/code&gt;
sockets on a server&lt;/strong&gt;. Queue length and the other sockets states might also have
been useful under differing circumstances.&lt;/p&gt;

&lt;p&gt;According to &lt;a href=&quot;http://www.cs.northwestern.edu/~agupta/cs340/project2/TCPIP_State_Transition_Diagram.pdf&quot;&gt;Gordon McKinney’s TCP/IP State Transition Diagram&lt;/a&gt;,
&lt;code class=&quot;highlighter-rouge&quot;&gt;CLOSE_WAIT&lt;/code&gt; indicates that the socket is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;… waiting for a connection termination request from the local user&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our case the &lt;em&gt;“local user”&lt;/em&gt; was the Node.js application. Adding &lt;code class=&quot;highlighter-rouge&quot;&gt;-p&lt;/code&gt; to the
netstat call printed the PID that owned the leaked sockets. The PID belonged to
our application.&lt;/p&gt;

&lt;p&gt;Somewhere in our application, sockets were being created for S3 requests, but
never cleaned up. These sockets were exhausting some unknown upper limit,
causing new requests to hang.&lt;/p&gt;

&lt;p&gt;Now we know where to look and our picture of the control path has a little more
detail. Here’s how it looked once the leaked sockets reached critical mass:&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;lightbox&quot; href=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/what-we-know-2.png&quot;&gt;
  &lt;img src=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/what-we-know-2.png&quot; alt=&quot;Sequence diagram&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We set about tracing the code path for S3 requests in our codebase and found
the following.&lt;/p&gt;

&lt;h2 id=&quot;aws-sdk-for-nodejs&quot;&gt;AWS SDK for Node.js&lt;/h2&gt;

&lt;p&gt;All of the connections to AWS S3 were being established via the AWS SDK for
Node.js.&lt;/p&gt;

&lt;p&gt;The SDK uses socket pooling for connections to S3. The default &lt;a href=&quot;http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-configuring-maxsockets.html&quot;&gt;maximum size of
the pool is 50&lt;/a&gt;.
If sockets were leaking and there is an upper boundary to the number of active
sockets, it stands to reason that new requests to S3 (in our case, for static
assets) would fail in some way once 50 sockets had leaked. The number 50
anecdotally matched the number of &lt;code class=&quot;highlighter-rouge&quot;&gt;CLOSE_WAIT&lt;/code&gt; sockets we had seen with
netstat.&lt;/p&gt;

&lt;p&gt;We also found that our version of the SDK had been inadvertently pinned in our
codebase to &lt;a href=&quot;https://github.com/aws/aws-sdk-js/blob/HEAD/CHANGELOG.md#2614&quot;&gt;v2.6.14&lt;/a&gt;,
which was released shortly before we relaunched thewest.com.au.&lt;/p&gt;

&lt;p&gt;Given the age of our SDK version, it was conceivable that the socket leak was
now a known issue. We trawled through GitHub issues, PRs and the SDK Changelog
and found a suspect note in the
&lt;a href=&quot;https://github.com/aws/aws-sdk-js/blob/HEAD/CHANGELOG.md#2500&quot;&gt;2.50.0&lt;/a&gt; release
notes:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;bugfix: Request: Updates node.js request handling to obey socket read timeouts
after response headers have been received. Previously timeouts were being
ignored once headers were received, sometimes causing connections to hang.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Inspecting the code also revealed a significant body of other work had also been
released concerning sockets.&lt;/p&gt;

&lt;p&gt;We formed a hypothesis that updating the AWS SDK would cause all sockets to be
cleaned up correctly or emit errors that could be safely handled. There were
strong coincidences that suggested this was our issue. Patching the SDK was low
risk, easily tested and definitely overdue, but…&lt;/p&gt;

&lt;h2 id=&quot;measurements&quot;&gt;Measurements&lt;/h2&gt;
&lt;p&gt;It was important that we started measuring these socket metrics before
implementing a fix. Before-and-after measurements are critical to validate any
fix, and they make for great graphics in blog posts (below).&lt;/p&gt;

&lt;p&gt;I wrote a Zabbix module named &lt;a href=&quot;https://github.com/cavaliercoder/zabbix-module-sockets&quot;&gt;zabbix-module-sockets&lt;/a&gt;
to capture the following metrics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;the number of sockets in each state&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;the sum of the receive queue of all sockets&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;the sum of the send queue of all sockets&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a little simulated load we were able to replicate the connection leak in
pre-production environments and measure the results in Zabbix.&lt;/p&gt;

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

&lt;p&gt;With a reasonable hypothesis, measurements in place and way to validate our plan
in pre-production, we deployed the updated AWS SDK to our pre-production
environments.&lt;/p&gt;

&lt;p&gt;It worked! The following figure from Zabbix shows the dramatic change in
behavior around June 2nd, when the fix was subsequently deployed to production.&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;lightbox&quot; href=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/recvq-fix.png&quot;&gt;
  &lt;img src=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/recvq-fix.png&quot; alt=&quot;Socket Recv-Q graph&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The gradual upward trend of the Receive Queue length (green) stops after the
production deployment and the queue length stabilizes. The earlier sawtooth
pattern you may observe is caused by application restarts that were triggered to
remediate the socket pool being exhausted.&lt;/p&gt;

&lt;p&gt;The following figure shows sockets states on the same server. You will notice
the thin, red slice of &lt;code class=&quot;highlighter-rouge&quot;&gt;CLOSE_WAIT&lt;/code&gt; sockets discontinues around the time of the
fix deployment.&lt;/p&gt;

&lt;p&gt;&lt;a class=&quot;lightbox&quot; href=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/state-fix.png&quot;&gt;
  &lt;img src=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/state-fix.png&quot; alt=&quot;Socket states graph&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hey, check out the value in the &lt;em&gt;max&lt;/em&gt; column for &lt;code class=&quot;highlighter-rouge&quot;&gt;CLOSE_WAIT&lt;/code&gt; sockets…&lt;/p&gt;

&lt;p&gt;Case closed.&lt;/p&gt;

&lt;h2 id=&quot;lesson-learned&quot;&gt;Lesson learned&lt;/h2&gt;
&lt;p&gt;In our context, we have to &lt;a href=&quot;https://xkcd.com/1428/&quot;&gt;&lt;em&gt;“move fast and break things”&lt;/em&gt;&lt;/a&gt;. Most things require
a trade-off between time-to-market and accrued technical debt. We aim to make
the best possible moves with the limited information and resources we have
available at any given time.&lt;/p&gt;

&lt;p&gt;That said, this event highlighted some priorities that could at least help us
to prevent similar issues in the future.&lt;/p&gt;

&lt;h3 id=&quot;alert-on-499-responses&quot;&gt;Alert on 499 responses&lt;/h3&gt;
&lt;p&gt;For HTTP servers that support HTTP 499 like nginx, a small number of
&lt;code class=&quot;highlighter-rouge&quot;&gt;499 Client Closed Request&lt;/code&gt; responses should be expected at the edge, where
clients do have the prerogative to disconnect prematurely. But in higher
frequency, or at other points in your signal chain, these responses might
indicate that timeouts are incorrectly configured. So…&lt;/p&gt;

&lt;h3 id=&quot;get-your-timeouts-right&quot;&gt;Get your timeouts right&lt;/h3&gt;
&lt;p&gt;Each component should be configured with a timeout that is longer than all its
upstream dependencies. By example, if you have a 3 second timeout on database
queries, then the application server should not cancel requests any sooner than
this. Otherwise the source of a problem is obscured and all you are presented
with is a timeout error from a component other that the one that misbehaved - as
we experienced.&lt;/p&gt;

&lt;p&gt;This solution is complicated by services that need multiple round-trips or have
asynchronous behaviors, but…&lt;/p&gt;

&lt;h3 id=&quot;everything-should-be-bounded&quot;&gt;Everything should be bounded&lt;/h3&gt;
&lt;p&gt;There is an expected operational behavior of every component in our system. Any
operation that completes too quickly or too slowly should create an error that
is meaningfully propagated, logged and alerted on.&lt;/p&gt;

&lt;p&gt;In this case, our Content API servers should have been configured with a tightly
constrained timeout on all requests to S3. Error handlers in our code would then
have sent a meaningful 5xx error back downstream.&lt;/p&gt;

&lt;p&gt;This principle should also apply to the incoming request to the API. We should
cancel any request that runs too long, &lt;em&gt;for any reason&lt;/em&gt;. This has operational
advantages, but is also good security practice to mitigate denial of service.&lt;/p&gt;

&lt;h3 id=&quot;health-checks-must-validate-all-dependencies&quot;&gt;Health checks must validate all dependencies&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/message/41926/&quot; target=&quot;_blank&quot;&gt;
  &lt;img class=&quot;inline&quot; src=&quot;http://cdn.cavaliercoder.com/blog/2017-05-27-a-webops-post-mortem/everything-fails.jpg&quot; alt=&quot;Everything fails, all the time&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of our micro-services has a health check route that quickly validates each
of its known dependencies (e.g. it can connect to the database, write to a log
file, etc.). In this case we had neglected to include S3 in the dependency
checks. As a result, each service was advertising itself as healthy.&lt;/p&gt;

&lt;p&gt;If we had included S3 in the health checks, the checks would have failed, these
instances would have been marked out of the load balancer, alerts would have
escalated and new healthy instances would have been rolled in automatically.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Under the stress of trying to restore services, we never felt any hunch to check
the battle-hardened AWS SDK for leaked sockets to the notoriously reliable AWS
S3 service. But this was our root cause.&lt;/p&gt;

&lt;p&gt;As unlikely as the issue was, having a response plan in place and sticking to it
gave us the best possible outcomes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;We followed the facts and found the root cause&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Services were restored quickly, without introducing unnecessary risk or
destroying evidence&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We uncovered other weaknesses in our systems that could be addressed before
they led to disaster&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We learned a whole lot about the software and systems we work with&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading! I’d love to hear your thoughts in the comments section
below.&lt;/p&gt;

&lt;h2 id=&quot;appendix-leaks-from-other-buckets&quot;&gt;Appendix: Leaks from other buckets&lt;/h2&gt;

&lt;p&gt;With our new socket monitoring in place in Zabbix, we noticed a much smaller
number of sockets to S3 being leaked on almost all of our application servers.&lt;/p&gt;

&lt;p&gt;We discovered that the AWS CodeDeploy Agent was the culprit. We use CodeDeploy
to deploy our Node.js applications and S3 is used as the storage backend.&lt;/p&gt;

&lt;p&gt;Fortunately, these sockets leak much slower (we’ve only seen up to 3 on any one
instance). For now, routine restarts will suffice until AWS are able to address
&lt;a href=&quot;https://github.com/aws/aws-codedeploy-agent/issues/115&quot;&gt;the issue&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Tue, 27 Jun 2017 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/webops-postmortem.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/webops-postmortem.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Restart services in docker-compose</title>
        <description>&lt;p&gt;The following shell function allows you to quickly restart one or more services
in a running &lt;code class=&quot;highlighter-rouge&quot;&gt;docker-compose&lt;/code&gt; service composition.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# restart services in docker-compose&lt;/span&gt;
docker-compose-restart&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
	docker-compose stop &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
	docker-compose rm &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
	docker-compose create &lt;span class=&quot;nt&quot;&gt;--force-recreate&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
	docker-compose start &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once loaded into your shell, you can call it with:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ docker-compose-restart [SERVICE...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I often have this requirement when using &lt;code class=&quot;highlighter-rouge&quot;&gt;docker-compose&lt;/code&gt; for development. If
a Docker image is updated, it’s inconvenient to restart all of the containers
managed by compose.&lt;/p&gt;

&lt;p&gt;This function will:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;stop the services&lt;/li&gt;
  &lt;li&gt;remove the associated containers and any anonymous volumes attached to them&lt;/li&gt;
  &lt;li&gt;recreate the containers from updated images&lt;/li&gt;
  &lt;li&gt;start the services again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any services not specified are left unchanged.&lt;/p&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Copy the function into your shell profile script (For BASH, this is
&lt;code class=&quot;highlighter-rouge&quot;&gt;~/.profile&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Restart your shell by logging out and in again&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;The following &lt;code class=&quot;highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file defines two services: &lt;code class=&quot;highlighter-rouge&quot;&gt;web&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;db&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;2'&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mongo&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mongo&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Suppose these services are running via &lt;code class=&quot;highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt;, but you have made
changes to the &lt;code class=&quot;highlighter-rouge&quot;&gt;nginx&lt;/code&gt; image which is used to run the &lt;code class=&quot;highlighter-rouge&quot;&gt;web&lt;/code&gt; service. These
changes are not apparent in the running containers.&lt;/p&gt;

&lt;p&gt;Restart the &lt;code class=&quot;highlighter-rouge&quot;&gt;web&lt;/code&gt; service as follows for your changes to be available:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ docker-compose-restart web
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Sat, 05 Nov 2016 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/restarting-services-in-docker-compose.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/restarting-services-in-docker-compose.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Zabbix Conference 2016</title>
        <description>&lt;iframe id=&quot;ytplayer&quot; type=&quot;text/html&quot; width=&quot;740&quot; height=&quot;420&quot; src=&quot;https://www.youtube.com/embed/nlk3nMHy188?autoplay=1&amp;amp;origin=http://cavaliercoder.com&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;a href=&quot;http://cdn.cavaliercoder.com/blog/2016-09-18-zabbix-conference-2016/slides.pdf&quot; target=&quot;_blank&quot;&gt;
    &lt;img class=&quot;right&quot; src=&quot;http://cdn.cavaliercoder.com/blog/2016-09-18-zabbix-conference-2016/slides.png&quot; alt=&quot;Download slide deck&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m finally recovering from jet lag after landing home from Zabbix Conference
2016 in Riga, Latvia. My good mate and colleague, James Cook and I had the
pleasure of attending the conference this year to discover what’s new with
Zabbix, how others are making use of it, and most importantly, to meet and greet
with the superb team at Zabbix SIA HQ.&lt;/p&gt;

&lt;p&gt;After my talk at &lt;a href=&quot;/blog/monitoring-6000-hosts-in-zabbix.html&quot;&gt;Perth DevOps Meetup&lt;/a&gt;,
Zabbix were kind enough to ask me to give a similar presentation at the
conference. You can &lt;a href=&quot;http://cdn.cavaliercoder.com/blog/2016-09-18-zabbix-conference-2016/slides.pdf&quot;&gt;download a PDF copy here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My talk covered a little about Kinetic IT and our client, the WA Department of
Education. I then described some of the automation tools we have written to
turn Zabbix into a fairly autonomous monitoring system. Finally I gave a
demonstration of a selection of the automation tools that I have released or
rewritten for open source.&lt;/p&gt;

&lt;p&gt;Please also, check out the talks for all the other speakers at the
&lt;a href=&quot;http://www.zabbix.com/conf2016_agenda.php&quot;&gt;Zabbix Conference 2016 homepage&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions or comments, start the discussion below or get in
touch with me on &lt;a href=&quot;https://twitter.com/cavaliercoder&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Sun, 18 Sep 2016 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/zabbix-conference-2016.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/zabbix-conference-2016.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Monitoring 6000+ hosts in Zabbix</title>
        <description>&lt;p&gt;&lt;a href=&quot;http://cdn.cavaliercoder.com/blog/2016-05-26-monitoring-6000-hosts-in-zabbix/slides.pdf&quot; target=&quot;_blank&quot;&gt;
    &lt;img class=&quot;right&quot; src=&quot;http://cdn.cavaliercoder.com/blog/2016-05-26-monitoring-6000-hosts-in-zabbix/slide1.png&quot; alt=&quot;Download slide deck&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last night I had the privilege of speaking to the
&lt;a href=&quot;http://www.meetup.com/DevOps-Perth/&quot;&gt;Perth DevOps Meetup&lt;/a&gt; group about our
journey delivering a large scale monitoring project with Zabbix. Thanks to
everyone who came long and for the great questions afterwards.&lt;/p&gt;

&lt;p&gt;Click the slide deck to &lt;a href=&quot;http://cdn.cavaliercoder.com/blog/2016-05-26-monitoring-6000-hosts-in-zabbix/slides.pdf&quot;&gt;download a PDF copy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions or comments, start the discussion below or get in
touch with me on &lt;a href=&quot;https://twitter.com/cavaliercoder&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 26 May 2016 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/monitoring-6000-hosts-in-zabbix.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/monitoring-6000-hosts-in-zabbix.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Downloading large files in Go</title>
        <description>&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;net/http&lt;/code&gt; package bundled with Go provides a really solid HTTP
implementation which excels particularly as a base for HTTP based API clients
and servers.&lt;/p&gt;

&lt;p&gt;If you’re writing software which needs to download large files from the
Internet, such as ISO images or software packages, you may need to implement
some client side logic to overcome some more use-case specific challenges such
as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;multiple concurrent downloads&lt;/li&gt;
  &lt;li&gt;naming of downloaded files&lt;/li&gt;
  &lt;li&gt;UI feedback with progress indicators&lt;/li&gt;
  &lt;li&gt;clean cancellation of running downloads&lt;/li&gt;
  &lt;li&gt;resuming of interrupted downloads&lt;/li&gt;
  &lt;li&gt;validating downloaded files using checksums&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article will step you through using a Go package called
&lt;a href=&quot;http://github.com/cavaliercoder/grab&quot;&gt;grab&lt;/a&gt; which abstracts &lt;code class=&quot;highlighter-rouge&quot;&gt;net/http&lt;/code&gt; to
provide these features. We’ll build a simple ‘wget’-like binary to make use of
all such awesomeness.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;grab&lt;/code&gt; provides convenience methods &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Get&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.GetAsync&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.GetBatch&lt;/code&gt; for simple operations. When you need more control over the
HTTP session, you can use a &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Client&lt;/code&gt; and configure it to your needs.&lt;/p&gt;

&lt;p&gt;Examples for these functions and using a custom client are included below.&lt;/p&gt;

&lt;p&gt;To get started, install the &lt;code class=&quot;highlighter-rouge&quot;&gt;grab&lt;/code&gt; package with:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ go get github.com/cavaliercoder/grab
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;download-a-file&quot;&gt;Download a file&lt;/h2&gt;

&lt;p&gt;The simplest way to download a file is using &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Get&lt;/code&gt;. It accepts two
parameters; a destination file path and the source URL. &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Get&lt;/code&gt; uses
&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.DefaultClient&lt;/code&gt; as a HTTP client which has default settings. It will follow
redirect responses from remote servers and use a corporate proxy if configured
on the host system. Essentially, &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Get&lt;/code&gt; is a wrapper for
&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.DefaultClient.Do&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You may specify an existing or non-existing file path as the destination or you
may specify an existing directory.&lt;/p&gt;

&lt;p&gt;If a directory is given as the destination, &lt;code class=&quot;highlighter-rouge&quot;&gt;grab&lt;/code&gt; will determine the filename
using &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Disposition&lt;/code&gt; headers if they are returned by the remote server or
extract a filename from the source URL. If either of these features fails, an
error is returned which can be identified using &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.IsNoFilename&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the destination filename exists, &lt;code class=&quot;highlighter-rouge&quot;&gt;grab&lt;/code&gt; assumes it is a complete or partially
complete download and will resume downloading from the end of the file if
supported by the remote server. Otherwise the file will be overwritten.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Get&lt;/code&gt; and all other download functions return a &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Response&lt;/code&gt; which
includes context about the downloaded file; including the path where the file
was saved.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Get&lt;/code&gt; is a blocking, synchronous operation, which means that the function
does not return a response until the download is complete or encounters an
error. This is not terribly useful for lengthy downloads so I’ll solve this
problem a little further down.&lt;/p&gt;

&lt;p&gt;First, the following example will create a simple binary which will download a
source file from a URL specified on the command line and save it to the current
working directory (&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;.&quot;&lt;/code&gt;).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;github.com/cavaliercoder/grab&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;os&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// get URL to download from command args&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&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;.&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;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;usage: %s url&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&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;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&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;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// download file&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Downloading %s...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error downloading %s: %v&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Successfully downloaded to %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&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;Build it with:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ go build -o grab-example
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and run it with:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ./grab-example http://some-url
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;add-progress-updates&quot;&gt;Add progress updates&lt;/h2&gt;

&lt;p&gt;The simple example above will download a file but it is not practical for
lengthy downloads which should provide some feedback to the user with the
progress of the download.&lt;/p&gt;

&lt;p&gt;The following example uses &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.GetAsync&lt;/code&gt; which immediately returns a channel
which will receive a &lt;code class=&quot;highlighter-rouge&quot;&gt;*grab.Response&lt;/code&gt; and close as soon as the download has been
negotiated with the remote server, before the file transfer has started.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.GetAsync&lt;/code&gt; is a wrapper for &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.DefaultClient.DoAsync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once the response is received, it can be polled periodically to monitor the
progress of the file transfer until it is finished. This example simply prints a
progress update every 200ms.&lt;/p&gt;

&lt;p&gt;All of the &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Response&lt;/code&gt; methods are thread-safe and atomic.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;github.com/cavaliercoder/grab&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;os&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// get URL to download from command args&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&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;.&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;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;usage: %s url&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&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;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&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;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// start file download&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Downloading %s...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;respch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error downloading %s: %v&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// block until HTTP/1.1 GET response is received&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Initializing download...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;respch&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// print progress until transfer is complete&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsComplete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[1AProgress %d / %d bytes (%d%%)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[K&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BytesTransferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&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;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Millisecond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// clear progress line&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[1A&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[K&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// check for errors&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error downloading %s: %v&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Successfully downloaded to ./%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&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;h2 id=&quot;batch-downloads&quot;&gt;Batch downloads&lt;/h2&gt;

&lt;p&gt;The next example allows multiple URLs to be given on the command line and
downloaded simultaneously using &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.GetBatch()&lt;/code&gt; which is a wrapper for
&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.DefaultClient.DoBatch()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Files will be transferred three at a time, as &lt;code class=&quot;highlighter-rouge&quot;&gt;3&lt;/code&gt; is given for the worker count
parameter in the call to &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Getbatch()&lt;/code&gt;. To download all files immediately
(one worker per request), simply give &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; as the worker count parameter. Each
download will be saved to the current working directory as &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;.&quot;&lt;/code&gt; is given as the
destination parameter.&lt;/p&gt;

&lt;p&gt;With a batch operation we don’t have immediate access to any &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Response&lt;/code&gt;.
These will be sent via the channel returned by &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.GetBatch()&lt;/code&gt; each time a
worker starts a requested URL. We don’t know when all of these responses will
arrive, but we want to monitor downloads which are already in process so we
create a &lt;code class=&quot;highlighter-rouge&quot;&gt;for&lt;/code&gt; loop and &lt;code class=&quot;highlighter-rouge&quot;&gt;select&lt;/code&gt; between two channels; one to receive responses
and the other (the ticker) to periodically print the status of the responses
which have already been received.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;github.com/cavaliercoder/grab&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;os&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// get URL to download from command args&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&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;.&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;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;usage: %s url [url]...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&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;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&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;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// start file downloads, 3 at a time&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Downloading %d files...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;respch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%v&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// start a ticker to update progress every 200ms&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewTicker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Millisecond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// monitor downloads&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make&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;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;respch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// a new response has been received and has started downloading&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// (nil is received once, when the channel is closed by grab)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// clear lines&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[%dA&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[K&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// update completed downloads&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsComplete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// print final result&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
						&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error downloading %s: %v&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
						&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Finished %s %d / %d bytes (%d%%)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BytesTransferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&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;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

					&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// mark completed&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// update downloads in progress&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Downloading %s %d / %d bytes (%d%%)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[K&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BytesTransferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&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;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%d files successfully downloaded.&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;x&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;h2 id=&quot;customizing-requests&quot;&gt;Customizing requests&lt;/h2&gt;

&lt;p&gt;None of the previous convenience methods offer any control over the HTTP request
or transport. Much like the &lt;code class=&quot;highlighter-rouge&quot;&gt;net/http&lt;/code&gt; package, &lt;code class=&quot;highlighter-rouge&quot;&gt;grab&lt;/code&gt; enable such controls as
well as additional features such as checksum validation via &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Client&lt;/code&gt;,
&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Request&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Response&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the same way that the &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Get*&lt;/code&gt; methods work behind the scenes, using
a client requires that you define and configure a &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Client&lt;/code&gt;, one or more
&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Requests&lt;/code&gt; and pass them to one of the &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Client.Do*&lt;/code&gt; methods which
then return a &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Response&lt;/code&gt; for each request.&lt;/p&gt;

&lt;p&gt;Let’s take a look at some of the configuration options available when creating
a download request in the following example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;c&quot;&gt;// create a download request&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://some-url/my-file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;panic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// set destination file path&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filename&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;./my-file&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// set expected file size if known&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// set expected file checksum if known&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DecodeString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;b982505fc48ea2221d163730c1856770dc6579af9eb73c997541c4ac6ecf20a9&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetChecksum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sha256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// delete the downloaded file if it fails checksum validation&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RemoveOnError&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// request a notification when the download is completed (successfully or&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// otherwise)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotifyOnClose&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You may also configure the HTTP request itself, including request headers,
cookies, authentication, etc. using the &lt;code class=&quot;highlighter-rouge&quot;&gt;http.Request&lt;/code&gt; nested in the
&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Request.HTTPRequest&lt;/code&gt; field, as demonstrated in the following code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;c&quot;&gt;// set custom HTTP method&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTPRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// set request headers&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTPRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;X-SOME-HEADER&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Custom value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// set a cookie&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTPRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddCookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// set basic HTTP authentication headers&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTPRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetBasicAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;using-a-custom-client&quot;&gt;Using a custom Client&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;grab&lt;/code&gt; provides a default client, &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.DefaultClient&lt;/code&gt; which is used by each of
the &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Get*&lt;/code&gt; methods. If you wish to customize HTTP transport rules such
as connection timeouts, proxy configuration, redirect policies, etc. you may
create a custom client with &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.NewClient&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The following code includes examples of customizing a client:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;c&quot;&gt;// create a custom client&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// set the user agent string for all HTTP requests&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UserAgent&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MyApp&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// set a custom connection timeout&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTPClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// replace the HTTP client with one that bypasses any system proxy settings&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HTTPClient&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Transport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Proxy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;x&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;Once you have configured a client and some requests, you pass the requests to
whichever of the &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Do*&lt;/code&gt; methods best match your use case. These methods
are synonymous with the &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Get*&lt;/code&gt; methods and behave as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Client.Do&lt;/code&gt; - blocks and returns a response once the download is
completed or an error occurs&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Client.DoAsync&lt;/code&gt; - immediately returns a channel which will receive a
single &lt;code class=&quot;highlighter-rouge&quot;&gt;*grab.Response&lt;/code&gt; and close as soon as the download has been negotiated
with the remote server; before the transfer has started&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;grab.Client.DoBatch&lt;/code&gt; - accepts multiple requests and executes them
simultaneously. It accepts a &lt;code class=&quot;highlighter-rouge&quot;&gt;workers&lt;/code&gt; parameter which determines how many
downloads will be in process at any given time, while the remaining requests
are queued until a worker is available. It returns a channel which will
receive a &lt;code class=&quot;highlighter-rouge&quot;&gt;grab.*Response&lt;/code&gt; for each request and close once they are all
sent. The responses are sent through the channel as soon as the download has
been negotiated with the remote server; before the transfer has started&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following and final example uses a custom client to download a batch of
files with periodic progress updates:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;github.com/cavaliercoder/grab&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;os&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// get URL to download from command args&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&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;.&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;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;usage: %s url [url]...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&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;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// create a custom client&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UserAgent&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Grab example&quot;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// create requests from command arguments&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reqs&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make&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;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&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;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%v&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

		&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reqs&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// start file downloads, 3 at a time&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Downloading %d files...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;respch&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoBatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reqs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// start a ticker to update progress every 200ms&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewTicker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Millisecond&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// monitor downloads&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make&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;grab&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;respch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// a new response has been received and has started downloading&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// (nil is received once, when the channel is closed by grab)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// clear lines&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[%dA&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[K&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// update completed downloads&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsComplete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// print final result&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
						&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&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;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error downloading %s: %v&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
						&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Finished %s %d / %d bytes (%d%%)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BytesTransferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&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;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

					&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// mark completed&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

			&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// update downloads in progress&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inProgress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
					&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Downloading %s %d / %d bytes (%d%%)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33[K&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BytesTransferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&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;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Progress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%d files successfully downloaded.&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Tue, 19 Jan 2016 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/downloading-large-files-in-go.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/downloading-large-files-in-go.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>The Goal: A challenge to IT Operations</title>
        <description>&lt;p&gt;This week I took an adventure in 1980’s manufacturing plant optimization in
Eliyahu M. Goldratt’s “&lt;a href=&quot;http://www.amazon.com/The-Goal-Process-Ongoing-
Improvement/dp/0884270610&quot;&gt;The Goal: A process of ongoing
improvement&lt;/a&gt;”. The book is featured in discussions around DevOps,
particularly concerning its heavy reference in Kim, Behr and Stafford’s &lt;a href=&quot;http://itrevolution.com/books/phoenix-project-devops-book/&quot;&gt;The
Phoenix Project&lt;/a&gt; and
all things Lean.&lt;/p&gt;

&lt;p&gt;It was an unusual read for me but has certainly changed the way I approach
problems in IT operations and even in getting my daughter to school on time.&lt;/p&gt;

&lt;p&gt;In this post, I’ll aim to condense some of the principals taught in the book,
their application in DevOps and finally offer a challenge to IT organizations
where application deployment is &lt;em&gt;not&lt;/em&gt; the primary line of business&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The Goal&lt;/em&gt; is a fictional business parable that follows Alex Rogo, a
manufacturing plant manager at UniCo manufacturing. His plant (and the business
as a whole) are deep in the red, despite employing some of the best management
wizardry of cost cutting exercises, order expedition, localized optimizations
and even fancy new robots.&lt;/p&gt;

&lt;p&gt;Alex has a chance meeting with a physics professor, Jonah, from his university
days, who challenges some of Alex’s (and my own) most basic assumptions about
productivity and the reason the business exists at all. Jonah leads Alex, via
provocative Socratic inquiry, to approach his manufacturing and personal issues
scientifically.&lt;/p&gt;

&lt;p&gt;The result for Alex was saving the business and some general kickassery.&lt;/p&gt;

&lt;p&gt;I was surprised how entertaining the book was. For me the subject matter of the
plant was fairly foreign and uninteresting but the story telling is very human
and relatable.&lt;/p&gt;

&lt;p&gt;Manufacturing jargon is kept to a minimum, with only enough detail given to
solidify some of the abstract ideas being taught.&lt;/p&gt;

&lt;p&gt;The book didn’t feel dated at all. The only reminder that we are not in the
current millennium is the mention of dot-matrix printers and smoking in the
office.&lt;/p&gt;

&lt;p&gt;Being a work of fiction, I did doubt whether the scale of some of the successes
encountered in the book could be replicated in reality. While the turnaround of
the fictional UniCo is extreme, the common-sense nature of the science described
lends credibility. Fortunately for us, there is now also a long history of well
published successes and failures with Eliyahu’s process and similar ideas from
DevOps, the Toyota Production System or Lean manufacturing.&lt;/p&gt;

&lt;h2 id=&quot;socratic-inquiry&quot;&gt;Socratic inquiry&lt;/h2&gt;

&lt;p&gt;All of the lessons taught in the book could be condensed into a two or three
page cheat sheet; even with substantial context. Unfortunately, some of the
lessons are so contrary to our modus operandi that in this form they would read
as nonsense; much like the notion of the world being round once did.&lt;/p&gt;

&lt;p&gt;For example, Jonah suggests that &lt;em&gt;most&lt;/em&gt; efficiencies gained in local areas of
production actually serve to cripple the entire system, contrary to my
intuition.&lt;/p&gt;

&lt;p&gt;The beauty of the relationship between Jonah and Alex, is that Jonah never
provides answers to anything. He only provides questions and validation of
Alex’s assertions. In doing so, much like the Greek philosopher
&lt;a href=&quot;https://en.wikipedia.org/wiki/Socratic_method&quot;&gt;Socrates&lt;/a&gt;, Jonah teaches Alex
how to think. Essentially, Jonah could give Alex a fish and feed him for a day,
but instead teaches Alex how to fish, so Alex can own his own destiny.&lt;/p&gt;

&lt;p&gt;This form of dialog caused me to continually consider my own situation. Clearly
the nuances of manufacturing do not directly apply to IT operations, so some of
the answers Alex discovers have no relevance to me (I don’t use heat treat ovens
in the datacenter). Instead the challenges Jonah offers to my assumptions and
way of thinking lead me to answers of my own.&lt;/p&gt;

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

&lt;p&gt;The first question Jonah poses to Alex is “What is the goal”? In my position in
IT Operations, I might have answered “uptime” or “minimizing costs” or
“information security” or “good customer service”. Jonah highlights that these
sorts of answers are actually just a means to an end, not the end itself.&lt;/p&gt;

&lt;p&gt;For UniCo manufacturing, the &lt;em&gt;end&lt;/em&gt;, or the goal, is to make money. I work for an
outsourcing company, who have assigned me to a government body concerned with
education. For me the goal is a little unclear, so I’ll give it some more
thought and perhaps conversation with the powers-at-be. Is the goal to make
money for my employer? Or to improve education for my client? Or to make money
for my employer by improving education for my client? Or is the goal to assist
the current government in winning the next election (not educational outcomes)
so we can keep the client and continue to make money?&lt;/p&gt;

&lt;p&gt;The point is, without clarity on the goal, you might unwittingly work to
undermine it. For example, UniCo purchased new welding robots that improved
local efficiency, but actually impacted negatively on their goal; to make money.&lt;/p&gt;

&lt;p&gt;Once the goal is clear, Alex devises a succinct list of critical measures
of progress towards the goal which are used to classify everything in the plant:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Throughput&lt;/strong&gt;: the rate at which the system generates money though completed
sales&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Inventory&lt;/strong&gt;: the money currently invested in assets it intends to sell
(unfinished products, work-in-process, etc.)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Operational expenses&lt;/strong&gt;: money spent to turn inventory into throughput
(wages, power, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These measures are specific to UniCo’s goal of making money through
manufacturing but could be abstracted to other applications.&lt;/p&gt;

&lt;p&gt;For example in education, throughput might be the number of student who graduate
and proceed into successful work placements, inventory might be the number of
students currently in the system and operational expenses remain; the cost of
employing staff and keeping the lawns.&lt;/p&gt;

&lt;h2 id=&quot;dependent-events-statistical-fluctuations&quot;&gt;Dependent events, statistical fluctuations&lt;/h2&gt;

&lt;p&gt;Jonah highlights that a manufacturing plant represents a sequence (or many
sequences) of dependent events. Meaning, some events cannot happen until
preceding events have completed. E.g. assembly cannot begin until all required
parts have arrived. Each step in the process is also subject to statistical
fluctuations; where the duration of each execution of a discrete process will
fluctuate. Welding or painting a part, will always vary in time.&lt;/p&gt;

&lt;p&gt;This is fairly self-evident, and like me, Alex assumes that the &lt;em&gt;average&lt;/em&gt;
execution time of each production line station will determine the throughput of
the system (i.e. time from customer order to dispatch and receipt). In practice
however, the plant is failing to keep up and the issue is compounding over time.&lt;/p&gt;

&lt;p&gt;While hiking a group of scouts in single file through a nature trail, Alex
becomes frustrated that the group of boys cannot seem to stay together and are
running behind time to complete the hike. Alex identifies that the hike is much
like his plant:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;there is a goal: everyone arriving on time, all together&lt;/li&gt;
  &lt;li&gt;one boy cannot move forward until the next boy has moved forward&lt;/li&gt;
  &lt;li&gt;each step of each boy fluctuates in distance and duration&lt;/li&gt;
  &lt;li&gt;the distance covered by the last hiker is the throughput of the system&lt;/li&gt;
  &lt;li&gt;the energy expended is the operational expense of the system&lt;/li&gt;
  &lt;li&gt;the distance between the first and last hiker, is the inventory or
work-in-process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Much like the factory, the work-in-process is in continual growth, with the
distance between front and back of the line constantly expanding, increasing
risk and the energy required to compensate for the distance.&lt;/p&gt;

&lt;p&gt;Alex’s expectation was that the average pace of the boy scouts would determine
the arrival time. Instead he discovers, that it’s actually the maximum
accumulated, negative deviation from the average that determines the throughput
(arrival time) and inventory (distance between front and back) of the group.&lt;/p&gt;

&lt;p&gt;Each step that each boy takes will deviate slightly from the average. Every time
a deviation is in the negative (slightly slower), that deviation accumulates as
the boys behind cannot pass and slow down. Additionally the distance between the
hiker in front increases. Compensating for this phenomena, requires significant
effort and is often outside the capacity of the boys.&lt;/p&gt;

&lt;p&gt;Alex further models the problem by passing matches along a sequence of bowls,
based on the fluctuating rolls of a dice. The throughput of the last bowl is
significantly lower than anticipated.&lt;/p&gt;

&lt;p&gt;This misconception about the capacity of the hikers being based on average pace
is shared at the UniCo plant and in my own estimation.&lt;/p&gt;

&lt;h2 id=&quot;the-illusion-of-local-efficiency&quot;&gt;The illusion of local efficiency&lt;/h2&gt;

&lt;p&gt;What Alex learns (and Jonah later validates) is that the whole process is
actually hinged on the bottleneck (or constraint), the slowest boy scout hiker.
Alex forces all of the boys to maintain pace with the slowest boy, young Herbie,
by placing Herbie at the front of the pack. As a result, the boys behind start
operating below capacity which in turn leads to the entire pack walking closely
together (a reduction in inventory or work-in-process). The boys are no longer
required to jog to catch up the gaps between them and so operational expenditure
is reduced.&lt;/p&gt;

&lt;p&gt;Now that Herbie is setting the pace for the group, his capacity gets greater
scrutiny and the group realize Herbie is carrying several kilos of obscure
camping equipment in his ruck sack. The equipment (or load) is distributed
amongst the boys with higher capacity and suddenly the entire group is moving
quicker (throughput)!&lt;/p&gt;

&lt;p&gt;Prior to understanding this metaphor, I might have hinged the success of the
group on the fastest kids (highest capacity resources). Ultimately, any
efficiency gained in the faster boys (strength, speed, etc.) is a complete and
utter waste when considering the goal of arriving on time and together.&lt;/p&gt;

&lt;p&gt;Likewise, the robots purchased by UniCo were a waste. Because of their
impressive capacity, they only served to increase inventory (by piling up
products in front of the next work station) and operational cost (staff working
overtime to catch up). The robots did increase the local efficiency of their work
centers, but undermined the goal of the company, to make money, by increasing
inventory and operational expenditure without increasing throughput of the
entire system.&lt;/p&gt;

&lt;h2 id=&quot;the-process-of-ongoing-improvements&quot;&gt;The process of ongoing improvements&lt;/h2&gt;

&lt;p&gt;The author provides a process to solve these problems, which in the parable, is
devised by Alex and his team of colleagues. This is the process Alex used to
rectify the hiking boy scouts, and his manufacturing plant woes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Identify the system’s primary bottleneck/s (Herbie, or the plant’s heat treat
machine)&lt;/li&gt;
  &lt;li&gt;Decide how to exploit the bottleneck (move Herbie to the front or keep the
heat treat machine running during lunch breaks)&lt;/li&gt;
  &lt;li&gt;Subordinate everything else to the above decision (all kids slow down to
Herbie’s pace and all other workstations prioritize parts bound for the heat
treat machine and remain idle if there are sufficient parts ready)&lt;/li&gt;
  &lt;li&gt;Elevate the system’s constraints (distribute the contents of Herbie’s
rucksack or install additional heat treat machines)&lt;/li&gt;
  &lt;li&gt;If a bottleneck is broken (i.e. no longer the bottleneck) repeat from step 1,
but don’t allow inertia to become a constraint (in this case, market demand
became the new bottleneck for Alex’s plant, but inertia caused them to
stockpile too many completed products)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;change&quot;&gt;Change&lt;/h2&gt;

&lt;p&gt;Once the manufacturing plant is once again profitable, Alex faces the challenge
of responding to changes in the marketplace and planning ahead to prevent any
further decline. With further prompting from his mentor Jonah, Alex determines
there are three critical challenges for managers in applying his new knowledge:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What needs to change?&lt;/li&gt;
  &lt;li&gt;What does it need to change &lt;strong&gt;to&lt;/strong&gt;?&lt;/li&gt;
  &lt;li&gt;How do you execute the change?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These steps are particularly critical when approaching changes to the most
difficult of all business resources to manage: people. When trying to change
the culture, values and thinking of people, the book highlights two important
strategies:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Socratic inquiry: leading people to answers by simply invoking curiosity.
Alex did this by sharing Jonah’s questions with his colleagues and working
together to find the answers.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The scientific method: create a hypothesis, A/B test the hypothesis (with a
control) and share the conclusion. Alex did this by comparing the results of
his hypothesis to the results attained in other UniCo plants, or simply with
“before and after” results.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;contribution-to-devops&quot;&gt;Contribution to DevOps&lt;/h2&gt;

&lt;p&gt;Prior to reading this book and The Phoenix Project, I would have described
DevOps as:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Enabling frequent production releases by making developers and sysadmins
play nice together and using some cool new automation tool chains.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I now have a completely different perspective. In fact, the cooperative culture
and tools have taken a heavy dive in significance for me. They are simply a
means to an end. They were a tailored response to a discrete problem. Truth is,
in my own experience in adopting &lt;a href=&quot;https://www.scriptrock.com/devops-
in-a-box&quot;&gt;“DevOps”&lt;/a&gt;, the CI/CD (Continuous Integration/Continuous Delivery) tools simply
became the overpriced, under performing, state-of- the-art, highly “efficient”
robots in Alex’s manufacturing plant.&lt;/p&gt;

&lt;p&gt;CI/CD tools and DevOps culture were born from a specific problem. That is, that
successful implementations of Agile development methodologies have significantly
increased the production capacity of development teams, typically without
exceeding marketplace demand. Historically, operations teams held the title of
“most efficient” as software releases were infrequent. Now the tables have
turned and operations has become a bottleneck in businesses achieving their
primary goal; make money first, fast and forever.&lt;/p&gt;

&lt;p&gt;DevOps addresses this problem, using Goldratt’s principals in the following
ways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Production deployment is identified as the bottleneck in achieving the goal&lt;/li&gt;
  &lt;li&gt;The bottleneck is “exploited” in that more frequent releases keep operations
functioning at full capacity. Operational activities that don’t directly
progress the goal are deprioritized.&lt;/li&gt;
  &lt;li&gt;Work is buffered in front of the bottleneck using “feature toggles” that
operations can enable via configuration when ready&lt;/li&gt;
  &lt;li&gt;Testing and quality assurance are moved before the bottleneck using Continuous
Integration and automated testing tools. This elevates the bottleneck by
preventing it from processing defective goods (i.e. buggy software)&lt;/li&gt;
  &lt;li&gt;Batch sizes are reduced, increasing the work of the testing, packaging and
release work centers, but increasing flow through the bottleneck (smaller
changes, lower risk, easier planning, etc.)&lt;/li&gt;
  &lt;li&gt;Automation is used to elevate the operations bottleneck. E.g. one touch
deployments, infrastructure as code, containers, etc.&lt;/li&gt;
  &lt;li&gt;As bottlenecks are defeated (i.e. ops can keep up with dev), other bottlenecks
(prioritized according to the goal) are identified and attacked such as
operational outages, technical debt, infrastructure projects, compliance
problems, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;application-in-it-operations&quot;&gt;Application in IT Operations&lt;/h2&gt;

&lt;p&gt;Deploying applications into production is actually a very small part of what my
organization does. Our focus is more in service desk, process management and
infrastructure services.&lt;/p&gt;

&lt;p&gt;Essentially, CI/CD is of very little (if any) value to us in achieving our
goals. For this reason, often my colleagues express that DevOps is also of very
little use to us. Eliyahu’s parable illustrates that the Theory of Constraints,
a core component of DevOps thinking, is actually more relevant than ever; it’s
just that the application will look different to CI/CD.&lt;/p&gt;

&lt;p&gt;Our goal, hypothetically, is to make money. We do this by increasing throughput
(projects delivered, requests fulfilled, etc.) while minimizing operational
expenses and inventory (incomplete works).&lt;/p&gt;

&lt;p&gt;Our bottleneck is not the frequent deployment of new software features. What we
need to determine, is where the primary bottleneck is in achieving our goal.
Hypothetically, it could be:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Service outages&lt;/li&gt;
  &lt;li&gt;Change and release management&lt;/li&gt;
  &lt;li&gt;Skills shortages&lt;/li&gt;
  &lt;li&gt;Quality assurance&lt;/li&gt;
  &lt;li&gt;Inter-team politics&lt;/li&gt;
  &lt;li&gt;Project delivery&lt;/li&gt;
  &lt;li&gt;Etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Improving any one of these in isolation could actually serve to undermine our
goal by increasing inventory or operational expenses, without increasing
throughput.&lt;/p&gt;

&lt;p&gt;The next step, the challenge to you and hopefully the subject of a future
article, is the application of Goldratt’s process of ongoing improvement to 
more diverse IT Operations organizations and overcoming the challenges of
organizational change.&lt;/p&gt;
</description>
        <pubDate>Sat, 12 Dec 2015 00:00:00 +0000</pubDate>
        <link>http://cavaliercoder.github.io/blog/the-goal.html</link>
        <guid isPermaLink="true">http://cavaliercoder.github.io/blog/the-goal.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
  </channel>
</rss>
