<atom:feed xmlns:atom="http://www.w3.org/2005/Atom"><atom:title type="html">Richard Crowley&amp;#8217;s blog</atom:title><atom:link href="http://rcrowley.org/index.xml" rel="self" /><atom:link href="http://rcrowley.org/" rel="alternate" /><atom:id>http://rcrowley.org/index.xml</atom:id><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:entry><atom:title type="html">DevStructure Redux
</atom:title><atom:link href="http://rcrowley.org/2015/12/13/devstructure-redux.html" rel="alternate" /><atom:id>http://rcrowley.org/2015/12/13/devstructure-redux.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">
&lt;article&gt;
&lt;h1&gt;DevStructure Redux&lt;/h1&gt;

&lt;p&gt;Five years ago Matt Tanase and I built a tool called &lt;a href="http://devstructure.com/blueprint/"&gt;Blueprint&lt;/a&gt; under the banner of a company we called DevStructure.&amp;nbsp; We believed configuring development environments was too difficult and that existing configuration management tools were too hard to use.&amp;nbsp; This, we concluded, resulted in development environments that were too far removed from their production environments and thus offered little proof that software will work well once deployed to production.&lt;/p&gt;

&lt;p&gt;We started with a paid semi-hosted development environment and eventually backed off to a set of open-source tools we hoped to support with production hosting offerings.&amp;nbsp; To cut a long story short, we never made it that far.&amp;nbsp; As a business, DevStructure was a failure.&lt;/p&gt;

&lt;p&gt;Early this morning while standing in the dark gently bouncing my two-month-old daughter back to sleep I was thinking about what we built and why we failed.&amp;nbsp; If I was ever bitter about our failure as a business I&amp;#8217;m long since over it but now I see the world through DevStructure-colored glasses; I see shades of what we tried to do in all sorts of tools being released all across our industry.&amp;nbsp; Perhaps that&amp;#8217;s confirmation bias but it keeps me thinking about how I&amp;#8217;d approach the problem today.&amp;nbsp; And let&amp;#8217;s be very clear:&amp;nbsp; The management of development environments that are reliable predictors of how software will behave in production environments is still a major threat to software quality five years later.&lt;/p&gt;

&lt;p&gt;However, since I adore &lt;a href="https://slack.com/jobs"&gt;my job&lt;/a&gt; and since chasing this idea would be a costly distraction for us right now, DevStructure Redux isn&amp;#8217;t going to be coming from me anytime soon.&amp;nbsp; Consider this a free idea or idle ramblings, whichever you choose.&lt;/p&gt;

&lt;p&gt;Blueprint was trying to put a better user interface on configuration management and even the idea of infrastructure as code but the tools we built were quite hostile towards code review.&amp;nbsp; Nowadays, I believe code review is essential so I&amp;#8217;ve resumed seeking code-centric user interface improvements.&lt;/p&gt;

&lt;p&gt;Today&amp;#8217;s configuration management suffers as a user interface in part because it is built on command-line tools readily available on the servers being managed.&amp;nbsp; Most operators can resist the temptation to &lt;code&gt;apt-get install ...&lt;/code&gt; in production but basically no one can resist in a development environment.&amp;nbsp; And why would they?&amp;nbsp; The edit-save-run cycle is a drag and over time most Chef or Puppet runs tend towards oh-my-god-this-is-slow.&lt;/p&gt;

&lt;p&gt;So this is where my mind was at 5 o&amp;#8217;clock this morning.&amp;nbsp; I thought that if configuration management was a concern of the operating system then the interface it ultimately exposed could be made more amenable to code review.&amp;nbsp; Here we go with the hand-waving.&lt;/p&gt;

&lt;p&gt;Code review-conducive package installation is the easiest to tackle.&amp;nbsp; Whatever the interface, there just needs to be a file or directory tree naming the packages to be installed and specifying their versions.&amp;nbsp; When changes are made the operating system needs to add and remove packages to suit.  Changes may be allowed via editor, command-line tool, or pulling changes from an upstream source code repository.&amp;nbsp; Hypothetically this could be as simple as files like &lt;code&gt;/etc/packages/&lt;em&gt;mysql-server&lt;/em&gt;&lt;/code&gt; that contain the desired version number.&amp;nbsp; Version control &lt;code&gt;/etc&lt;/code&gt; and send all the changes through code review.&lt;/p&gt;

&lt;p&gt;Immediately after installing a package folks tend to configure the newly-installed software.&amp;nbsp; Detecting meaningful changes to these files was always a major problem for Blueprint.&amp;nbsp; Well-behaved packages mark their configuration files and include hashes of the packaged content to enable detection of changes.&amp;nbsp; In my opinion we&amp;#8217;d be better-served by packages devoid of configuration files.&amp;nbsp; Programs would thus be required to function sanely by default or fail immediately at the author&amp;#8217;s discretion.&amp;nbsp; Imagine how wonderful it would be if this requirement were imposed on all packages:&amp;nbsp; &lt;code&gt;/etc&lt;/code&gt; would be empty on a fresh operating system installation!&amp;nbsp; If the defaults don't suit you, create &lt;code&gt;/etc/&lt;em&gt;my.cnf&lt;/em&gt;&lt;/code&gt; containing your desired configuration and commit it, too.&lt;/p&gt;

&lt;p&gt;While we&amp;#8217;re moving configuration files out of packages, let&amp;#8217;s get rid of maintainer scripts, too.&amp;nbsp; I want to control when services are started and restarted.&amp;nbsp; I want to control user and group IDs.&amp;nbsp; And I reserve the right to manage these actions and resources differently in my infrastructure than you do in yours.&amp;nbsp; Store service dependencies in &lt;code&gt;systemd&lt;/code&gt; units like &lt;code&gt;/etc/systemd/system/&lt;em&gt;mysql.unit&lt;/em&gt;&lt;/code&gt;.&amp;nbsp; Store users and groups in &lt;code&gt;/etc/passwd&lt;/code&gt; and &lt;code&gt;/etc/group&lt;/code&gt; but without all the cruft or better yet in &lt;code&gt;/etc/passwd.d&lt;/code&gt; and &lt;code&gt;/etc/group.d&lt;/code&gt; directories.&amp;nbsp; And version them just like packages and configuration files.&lt;/p&gt;

&lt;p&gt;Oddly, despite my desire to move maintainer scripts from packages out into configuration management, I also want to get rid of &lt;code&gt;exec&lt;/code&gt; or &lt;code&gt;execute&lt;/code&gt; resources that allow today&amp;#8217;s configuration management tools to run arbitrary commands.&amp;nbsp; Puppet&amp;#8217;s documentation has long said that if you find yourself using a lot of &lt;code&gt;exec&lt;/code&gt; resources you&amp;#8217;re wrong and should be writing your own types and providers.&amp;nbsp; Everyone ignored that because &lt;code&gt;exec&lt;/code&gt; resources are easy.&amp;nbsp; The real problem underlying all of this, though, is that it is easier to manage many aspects of a server today via commands than via files (&lt;code&gt;iptables&lt;/code&gt;(8), &lt;code&gt;mount&lt;/code&gt;(8), &lt;code&gt;sysctl&lt;/code&gt;(8), to name but a few) and tough to manage small changes to large sets of defaults (basically every default configuration file).&amp;nbsp; This needs to change if we're to make anything but superficial lipstick-on-a-pig progress.&amp;nbsp; It's no accident this is starting to sound a little bit like Plan 9.&lt;/p&gt;

&lt;p&gt;If we were starting DevStructure again today our company would look a whole lot more like CoreOS than like Chef or Puppet Labs.&amp;nbsp; My vision for a minimal operating system, however, is quite different than that of CoreOS.&amp;nbsp; I envision a base installation chock full of all the tools I could ever need to introspect the system.&amp;nbsp; I envision a package manager that enables and encourages fat packages, explicit dependency versioning and vendoring, and then gets out of the business of configuration and orchestration.&amp;nbsp; I envision configuration management being the only interface for adding and removing software or changing its configuration.&amp;nbsp; I envision that all aspects of the operating system will be designed to be version-controlled.&amp;nbsp; I envision packaging to be the obvious best way to deploy your own software into production.&lt;/p&gt;

&lt;p&gt;DevStructure Redux would sell an appliance or service (customer&amp;#8217;s choice) that turn this minimal operating system distribution into a streamlined development-to-production pipeline.&amp;nbsp; At a minimum, it would need to accept and serve package artifacts, integrate with test runners and code review tools, store package artifacts installed from the Internet so precise versions are available to all servers, manage server inventory, and orchestrate reconfiguration and deployments.&amp;nbsp; The toughest product challenge is, I think, how to infiltrate brownfield environments.&amp;nbsp; Failure is imminent if this product gives anyone the impression it&amp;#8217;s an all-or-nothing proposition; customers need to derive value immediately and in proportion to their investment.&lt;/p&gt;

&lt;p&gt;Alas, I will not have the opportunity to take up this cause again for a long time because I love my job.&amp;nbsp; Our development environments at Slack are not terribly portable to other applications or workflows but they are amazingly production-like and highly optimized for our 100+ engineers getting work done.&amp;nbsp; Seeing this scale does nothing but reinforce my belief that development environments should be as close of a match to production as possible.&amp;nbsp; I&amp;#8217;m not satisfied with the latest batch of abstractions over the ever more complex software systems being built today but I&amp;#8217;ll keep searching.&amp;nbsp; I sincerely hope someone builds what I&amp;#8217;ve described above because I&amp;#8217;d love to buy it.&lt;/p&gt;

&lt;/article&gt;

</atom:content></atom:entry><atom:entry><atom:title type="html">talks/gophercon-2014.html</atom:title><atom:link href="http://rcrowley.org/talks/gophercon-2014.html" rel="alternate" /><atom:id>http://rcrowley.org/talks/gophercon-2014.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">&lt;a href="http://rcrowley.org/talks/gophercon-2014.html"&gt;http://rcrowley.org/talks/gophercon-2014.html&lt;/a&gt;</atom:content></atom:entry><atom:entry><atom:title type="html">Certified
</atom:title><atom:link href="http://rcrowley.org/2014/04/15/certified.html" rel="alternate" /><atom:id>http://rcrowley.org/2014/04/15/certified.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">
&lt;article&gt;
&lt;header&gt;
	&lt;time datetime="2014-04-15" pubdate&gt;2014-04-15&lt;/time&gt;
	&lt;h1&gt;Certified&lt;/h1&gt;
&lt;/header&gt;

&lt;p&gt;It&amp;#8217;s a scary Internet out there.  Between the growing value of the data exchanged online and the ever-present threat that the NSA decides you&amp;#8217;re interesting we&amp;#8217;re left with only one responsible thing to do:  Protect the confidentiality and integrity of our and our users&amp;#8217; data.&lt;/p&gt;

&lt;p&gt;One important part of any strategy to protect our users&amp;#8217; data is encrypting that data when in motion and that means anywhere, anytime.  It&amp;#8217;s not good enough to only encrypt your payment pages.  It&amp;#8217;s not good enough to only encrypt traffic between users&amp;#8217; browsers and your load balancers.  Every connection that crosses a network should be made via TLS.  Period.&lt;/p&gt;

&lt;p&gt;Traditionally this is either confusing because OpenSSL or expensive because Verisign.  Today I&amp;#8217;m introducing a new option.  &lt;a href="https://github.com/rcrowley/certified"&gt;Certified&lt;/a&gt; will help you generate and manage an internal CA for your company and its &lt;a href="https://github.com/rcrowley/certified/wiki"&gt;wiki&lt;/a&gt; includes a growing collection of examples for the various operating systems, server softwares, and programming languages we all use.&lt;/p&gt;

&lt;p&gt;Be safe out there.&lt;/p&gt;

&lt;/article&gt;

</atom:content></atom:entry><atom:entry><atom:title type="html">talks/gosf-2014-01-15.html</atom:title><atom:link href="http://rcrowley.org/talks/gosf-2014-01-15.html" rel="alternate" /><atom:id>http://rcrowley.org/talks/gosf-2014-01-15.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">&lt;a href="http://rcrowley.org/talks/gosf-2014-01-15.html"&gt;http://rcrowley.org/talks/gosf-2014-01-15.html&lt;/a&gt;</atom:content></atom:entry><atom:entry><atom:title type="html">Go web services with Tiger Tonic
</atom:title><atom:link href="http://rcrowley.org/articles/tiger-tonic.html" rel="alternate" /><atom:id>http://rcrowley.org/articles/tiger-tonic.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">
&lt;article&gt;
&lt;header&gt;
	&lt;time datetime="2013-12-13" pubdate&gt;2013-12-13&lt;/time&gt;
	&lt;h1&gt;Go web services with Tiger Tonic&lt;/h1&gt;
&lt;/header&gt;

&lt;p&gt;&lt;em&gt;Cross-posted from &lt;a href="http://blog.gopheracademy.com/day-13-tiger-tonic"&gt;Go Advent Day 13 - Go web services with Tiger Tonic&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Go is unique among mainstream programming languages in that its standard library web server is not a complete afterthought.  The Go language is well-suited for engineering complex networked services and Go&amp;#8217;s standard library recognizes that many (if not most) of those services communicate via HTTP.  Sprinkle some &lt;a href="https://groups.google.com/forum/#!msg/golang-nuts/BNUNbKSypE0/E4qSfpx9qI8J"&gt;Google scale&lt;/a&gt; on it and your web applications and services can really hit the ground running.&lt;/p&gt;

&lt;p&gt;The standard library sets the tone but it&amp;#8217;s far from the end of the story of how to effectively build web services in Go.  And as most Go stories do, ours begins with an interface:&lt;/p&gt;

&lt;p&gt;&lt;pre&gt;&lt;code&gt;type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;http.Handler&lt;/code&gt; may not look like much but its simplicity is the key to its universality.  The standard library provides a number of powerful implementations of this interface but most serious web applications and services are quick to find shortcomings, differences of opinion, and higher-level abstractions that require us to implement &lt;code&gt;http.Handler&lt;/code&gt; ourselves.&lt;/p&gt;

&lt;h2&gt;Introducing Tiger Tonic&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/rcrowley/go-tigertonic"&gt;Tiger Tonic&lt;/a&gt; packages several &lt;code&gt;http.Handler&lt;/code&gt; implementations that make engineering web services easier.  They strive to be orthogonal like the features of the Go language itself.  And because everything is an &lt;code&gt;http.Handler&lt;/code&gt; it&amp;#8217;s possible and even easy to use Tiger Tonic in conjunction with your own or anyone else&amp;#8217;s handlers.&lt;/p&gt;

&lt;p&gt;Tiger Tonic eschews HTML templates, JavaScript and CSS asset pipelines, cookies, and the like to remain squarely focused on building JSON web services.  Why JSON?  It&amp;#8217;s become the lingua franca for serialization in web services everywhere.  Why the focus on web services?  The authors love &lt;a href="http://dropwizard.codahale.com"&gt;Dropwizard&lt;/a&gt; and missed all it brought to the Java community so they worked to reproduce that feeling in Go.&lt;/p&gt;

&lt;h2&gt;Multiplexing requests&lt;/h2&gt;

&lt;p&gt;Most web services respond differently to requests for different URLs so they reach for the standard &lt;code&gt;http.ServeMux&lt;/code&gt;.  Then these web services start handling requests like &lt;code&gt;POST /games/{id}/bet&lt;/code&gt; (an example from &lt;a href="https://developers.betable.com"&gt;Betable&lt;/a&gt;) and &lt;code&gt;http.ServeMux&lt;/code&gt; gets in the way.  &lt;code&gt;tigertonic.TrieServeMux&lt;/code&gt; is here to help.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;mux := tigertonic.NewTrieServeMux()
mux.Handle("POST", "/games/{id}/bet", handler)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;tigertonic.TrieServeMux&lt;/code&gt; multiplexes requests differently than the standard &lt;code&gt;http.ServeMux&lt;/code&gt;.  URL components surrounded by braces like &lt;code&gt;{id}&lt;/code&gt; above are wildcards added to &lt;code&gt;r.URL.Query&lt;/code&gt; and otherwise the request must match the method and path of the matching handler exactly.  In the author&amp;#8217;s experience this is the least surprising thing to do.  Plus it gives the framework the opportunity to respond 404 and 405 to requests it cannot fulfill.  See?  Your web service is already a good HTTP citizen!&lt;/p&gt;

&lt;p&gt;And as cool as that is it&amp;#8217;s even cooler that your web service is still an &lt;code&gt;http.Handler&lt;/code&gt; that you can &lt;code&gt;http.ListenAndServe&lt;/code&gt; without a second thought.&lt;/p&gt;

&lt;h2&gt;JSON in, JSON out&lt;/h2&gt;

&lt;p&gt;So now the question is how we respond to all the requests we&amp;#8217;re now multiplexing.  This is where we start to specialize on web services.  Once again, the standard library has giant shoulders on which to stand.  Import &lt;code&gt;encoding/json&lt;/code&gt; and you&amp;#8217;re off to the races, right?  Well:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;func handlerFunc(w http.ResponseWriter, r *http.Request) {
    err := json.NewEncoder(w).Encode(&amp;MyResponse{/*...*/})
    if nil != err {
        fmt.Fprintln(w, err)
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That&amp;#8217;s a bit clumsy.  &lt;code&gt;tigertonic.Marshaled&lt;/code&gt; allows you to construct handlers from functions that automatically deserialize request bodies according to the type of the final argument and serialize response bodies from the error or response object returned.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var handler http.Handler = tigertonic.Marshaled(func(
    url.URL, http.Header, *MyRequest,
) (int, http.Header, *MyResponse, error) {
    return http.StatusOK, nil, &amp;MyResponse{/*...*/}, nil
})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This proves to be a powerful higher-level abstraction that removes JSON serialization from the web service programmer&amp;#8217;s long list of concerns.  And just as before, handlers returned by &lt;code&gt;tigertonic.Marshaled&lt;/code&gt; understand how to respond 400, 406, and 415 like Roy Fielding intended.&lt;/p&gt;

&lt;h2&gt;Testing&lt;/h2&gt;

&lt;p&gt;Perhaps the best feature of this new abstraction is its effects on testing web services.  No disrespect to the &lt;code&gt;net/http/httptest&lt;/code&gt; package or &lt;code&gt;httptest.ResponseRecorder&lt;/code&gt; but Go web services should test their responses not their serializations.  For example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;func TestAdvent(t *testing.T) {
    s, _, rs, err := handler(
        mocking.URL(mux, "POST", "/games/ID/bet"),
        mocking.Header(nil),
        nil,
    )
    if nil != err {
        t.Fatal(err)
    }
    if http.StatusOK != s {
        t.Fatal(s)
    }
    if "win" != rs.Outcome { // Merry Christmas!
        t.Fatal(rs)
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That&amp;#8217;s far more precise than matching strings with regular expressions and far less verbose than deserializing the JSON response.&lt;/p&gt;

&lt;p&gt;For those keeping score at home: yes, &lt;code&gt;tigertonic.Marshaled&lt;/code&gt; also returns an &lt;code&gt;http.Handler&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Extra batteries&lt;/h2&gt;

&lt;p&gt;Though &lt;code&gt;tigertonic.TrieServeMux&lt;/code&gt; and &lt;code&gt;tigertonic.Marshaled&lt;/code&gt; are the main attractions, Tiger Tonic packages a number of other useful handlers that make building web services with Go easier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tigertonic.TrieServeMux&lt;/code&gt; has &lt;code&gt;HandleNamespace&lt;/code&gt; to match and remove prefixes from requested URLs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tigertonic.HostServeMux&lt;/code&gt; supports virtual hosting of many domains in a single Go process.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tigertonic.First&lt;/code&gt; and &lt;code&gt;tigertonic.If&lt;/code&gt; enable handler chaining a la Rack or WSGI middleware.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tigertonic.Counted&lt;/code&gt; and &lt;code&gt;tigertonic.Timed&lt;/code&gt; emit metrics about all your requests via &lt;a href="https://github.com/rcrowley/go-metrics"&gt;&lt;code&gt;go-metrics&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tigertonic.WithContext&lt;/code&gt; and &lt;code&gt;tigertonic.Context&lt;/code&gt; add support for strongly-typed per-request context.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tigertonic.HTTPBasicAuth&lt;/code&gt; is a specialization of &lt;code&gt;tigertonic.If&lt;/code&gt; that conditionally handles requests if an acceptable &lt;code&gt;Authorization&lt;/code&gt; header is present.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tigertonic.CORSBuilder&lt;/code&gt; and &lt;code&gt;tigertonic.CORSHandler&lt;/code&gt; facilitate setting the basic CORS response headers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tigertonic.Server&lt;/code&gt; has &lt;code&gt;CA&lt;/code&gt; and &lt;code&gt;TLS&lt;/code&gt; methods that simplify listening for TLS connections.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tigertonic.Configure&lt;/code&gt;, in conjunction with method values, makes it easy to read configuration files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Go forth&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/rcrowley/go-tigertonic"&gt;Tiger Tonic&lt;/a&gt; is available on GitHub an includes a complete &lt;a href="https://github.com/rcrowley/go-tigertonic/tree/master/example"&gt;example&lt;/a&gt; that covers all the handlers included.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http.Handler&lt;/code&gt; is the common currency for Go web frameworks of all shapes and sizes.  The handlers in the &lt;code&gt;tigertonic&lt;/code&gt; package are meant to make engineering web services more correct, more efficient, and more testable but because they&amp;#8217;re handlers they&amp;#8217;re at your service and up to the challenge just as long as you stick to the humble little &lt;code&gt;http.Handler&lt;/code&gt; interface.&lt;/p&gt;

&lt;/article&gt;

</atom:content></atom:entry><atom:entry><atom:title type="html">talks/basho-chat-2013-12-03.html</atom:title><atom:link href="http://rcrowley.org/talks/basho-chat-2013-12-03.html" rel="alternate" /><atom:id>http://rcrowley.org/talks/basho-chat-2013-12-03.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">&lt;a href="http://rcrowley.org/talks/basho-chat-2013-12-03.html"&gt;http://rcrowley.org/talks/basho-chat-2013-12-03.html&lt;/a&gt;</atom:content></atom:entry><atom:entry><atom:title type="html">talks/opsmatic-2013-11-19.html</atom:title><atom:link href="http://rcrowley.org/talks/opsmatic-2013-11-19.html" rel="alternate" /><atom:id>http://rcrowley.org/talks/opsmatic-2013-11-19.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">&lt;a href="http://rcrowley.org/talks/opsmatic-2013-11-19.html"&gt;http://rcrowley.org/talks/opsmatic-2013-11-19.html&lt;/a&gt;</atom:content></atom:entry><atom:entry><atom:title type="html">talks/strange-loop-2013.html</atom:title><atom:link href="http://rcrowley.org/talks/strange-loop-2013.html" rel="alternate" /><atom:id>http://rcrowley.org/talks/strange-loop-2013.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">&lt;a href="http://rcrowley.org/talks/strange-loop-2013.html"&gt;http://rcrowley.org/talks/strange-loop-2013.html&lt;/a&gt;</atom:content></atom:entry><atom:entry><atom:title type="html">talks/surge-2013.html</atom:title><atom:link href="http://rcrowley.org/talks/surge-2013.html" rel="alternate" /><atom:id>http://rcrowley.org/talks/surge-2013.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">&lt;a href="http://rcrowley.org/talks/surge-2013.html"&gt;http://rcrowley.org/talks/surge-2013.html&lt;/a&gt;</atom:content></atom:entry><atom:entry><atom:title type="html">talks/puppetconf-2013.html</atom:title><atom:link href="http://rcrowley.org/talks/puppetconf-2013.html" rel="alternate" /><atom:id>http://rcrowley.org/talks/puppetconf-2013.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">&lt;a href="http://rcrowley.org/talks/puppetconf-2013.html"&gt;http://rcrowley.org/talks/puppetconf-2013.html&lt;/a&gt;</atom:content></atom:entry><atom:entry><atom:title type="html">talks/agile-2013.html</atom:title><atom:link href="http://rcrowley.org/talks/agile-2013.html" rel="alternate" /><atom:id>http://rcrowley.org/talks/agile-2013.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">&lt;a href="http://rcrowley.org/talks/agile-2013.html"&gt;http://rcrowley.org/talks/agile-2013.html&lt;/a&gt;</atom:content></atom:entry><atom:entry><atom:title type="html">Graceful stopping in Go
</atom:title><atom:link href="http://rcrowley.org/articles/golang-graceful-stop.html" rel="alternate" /><atom:id>http://rcrowley.org/articles/golang-graceful-stop.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">
&lt;article&gt;
&lt;header&gt;
	&lt;time datetime="2013-04-27" pubdate&gt;2013-04-27&lt;/time&gt;
	&lt;h1&gt;Graceful stopping in Go&lt;/h1&gt;
&lt;/header&gt;
&lt;p&gt;It&amp;#8217;s rude to deploy early and often if deploys interrupt user requests so we build our Go services at Betable to stop gracefully without interrupting anyone.&amp;nbsp; The idea is to stop listening, presumably so a new process can take over, and let all open connections respond to any in-progress requests before finally stopping service.&amp;nbsp; Incidentally, we use &lt;a href="https://github.com/rcrowley/goagain"&gt;&lt;code&gt;goagain&lt;/code&gt;&lt;/a&gt; to restart without even stopping listening but that&amp;#8217;s beyond the scope of this article.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;main.main&lt;/code&gt; does four things: listens, makes a &lt;code&gt;Service&lt;/code&gt; and sends it into the background, blocks until signaled, and afterwards stops the service gracefully.&amp;nbsp; Listening and signal handling are right out of the standard library playbook except that, to my great annoyance, you need a &lt;code&gt;*net.TCPListener&lt;/code&gt; or &lt;code&gt;*net.TCPConn&lt;/code&gt; and not merely a &lt;code&gt;net.Listener&lt;/code&gt; or &lt;code&gt;net.Conn&lt;/code&gt; to call &lt;code&gt;SetDeadline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;service.Serve(listener)&lt;/code&gt; accepts connections and handles each one of them in its own goroutine.&amp;nbsp; It sets a deadline so &lt;code&gt;listener.AcceptTCP()&lt;/code&gt; doesn&amp;#8217;t block forever and between successive iterations checks to see if it should stop listening.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;service.serve(conn)&lt;/code&gt; reads and writes and likewise does so on a deadline.&amp;nbsp; It sets a deadline so &lt;code&gt;conn.Read(buf)&lt;/code&gt; doesn&amp;#8217;t block forever and between writing a response and reading the next request or after the &lt;code&gt;conn.Read(buf)&lt;/code&gt; deadline checks to see if it should close the connection.&lt;/p&gt;
&lt;p&gt;The various goroutines decide to close connections and listeners if the service&amp;#8217;s channel receives a value which, because nothing ever sends on this channel, is only possible after &lt;code&gt;service.Stop()&lt;/code&gt; has closed the channel.&amp;nbsp; Recall the built-in &lt;a href="http://golang.org/ref/spec#Close"&gt;&lt;code&gt;close&lt;/code&gt;&lt;/a&gt; function that causes channels to forevermore receive the zero-value of the channel&amp;#8217;s element type.&lt;/p&gt;
&lt;p&gt;The final piece of the puzzle is waiting for all goroutines to die which is implemented via the standard library&amp;#8217;s &lt;code&gt;sync.WaitGroup&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/rcrowley/5474430"&gt;https://gist.github.com/rcrowley/5474430&lt;/a&gt; has it all:&lt;/p&gt;
&lt;script src="https://gist.github.com/rcrowley/5474430.js"&gt;&lt;/script&gt;
&lt;/article&gt;

</atom:content></atom:entry><atom:entry><atom:title type="html">Go acknowledgement test
</atom:title><atom:link href="http://rcrowley.org/2013/04/19/golang-ack-test.html" rel="alternate" /><atom:id>http://rcrowley.org/2013/04/19/golang-ack-test.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">
&lt;article&gt;
&lt;header&gt;
	&lt;time datetime="2013-04-19" pubdate&gt;2013-04-19&lt;/time&gt;
	&lt;h1&gt;Go acknowledgement test&lt;/h1&gt;
&lt;/header&gt;
&lt;p&gt;I have been writing a lot of Go lately at &lt;a href="https://betable.com"&gt;Betable&lt;/a&gt; and the channels of channels pattern comes up so often that I broken down and benchmarked it.&amp;nbsp; I&amp;#8217;m publishig this more widely at the insistence of Paul Hammond who would agree that this is yet another of the many and persistent reminders that Rob Pike is smarter than I am.&lt;/p&gt;
&lt;p&gt;In summary, it is faster to allocate a one-time-use channel for a goroutine to acknowledge a request than to use a &lt;code&gt;sync.Mutex&lt;/code&gt;.&lt;p&gt;
&lt;script src="https://gist.github.com/rcrowley/5423530.js"&gt;&lt;/script&gt;
&lt;/article&gt;

</atom:content></atom:entry><atom:entry><atom:title type="html">talks/nodejs-2013-02-19/</atom:title><atom:link href="http://rcrowley.org/talks/nodejs-2013-02-19/" rel="alternate" /><atom:id>http://rcrowley.org/talks/nodejs-2013-02-19/</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">&lt;a href="http://rcrowley.org/talks/nodejs-2013-02-19/"&gt;http://rcrowley.org/talks/nodejs-2013-02-19/&lt;/a&gt;</atom:content></atom:entry><atom:entry><atom:title type="html">Federated Graphite
</atom:title><atom:link href="http://rcrowley.org/articles/federated-graphite.html" rel="alternate" /><atom:id>http://rcrowley.org/articles/federated-graphite.html</atom:id><atom:published>2015-12-14T06:51:31Z</atom:published><atom:updated>2015-12-14T06:51:31Z</atom:updated><atom:author><atom:name>Richard Crowley</atom:name></atom:author><atom:content type="html">
&lt;article&gt;
&lt;header&gt;
	&lt;time datetime="2012-08-03" pubdate&gt;2012-08-03&lt;/time&gt;
	&lt;h1&gt;Federated Graphite&lt;/h1&gt;
&lt;/header&gt;
&lt;p&gt;Graphite is a nearly undocumented thing of sweetness.  We at Betable collect, store, and graph many tens of thousands of metrics and we quickly reached the limits of a single-instance Graphite infrastructure.  That&amp;#8217;s OK, though, because Graphite does actually support some pretty reasonable federated architectures if you know where to look.  Consider this your treasure map.&lt;/p&gt;

&lt;p&gt;Aside: &lt;a href="https://gist.github.com/2311507"&gt;https://gist.github.com/2311507&lt;/a&gt; is how I build Debian packages for Graphite which I deploy via &lt;a href="https://github.com/rcrowley/freight"&gt;Freight&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Firewalls&lt;/h2&gt;

&lt;p&gt;You probably already have your firewalls configured to allow your entire infrastructure to send metrics to Graphite on port 2003.  Your Graphite almost-cluster is going to need a bit more permission to communicate with itself.&lt;/p&gt;

&lt;p&gt;Open ports 2014, 2114, and so on as you need between all Graphite nodes.  These are how the &lt;code&gt;carbon-relay.py&lt;/code&gt; instance on each Graphite node will communicate with the &lt;code&gt;carbon-cache.py&lt;/code&gt; instance(s) on each Graphite node.&lt;/p&gt;

&lt;p&gt;Also open the port on which the web app listens to other Graphite nodes.  This is how the web apps will communicate with the other web apps.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;carbon-cache.py&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Each &lt;code&gt;carbon-cache.py&lt;/code&gt; instance should listen on a non-loopback interface and on a port that&amp;#8217;s open to the &lt;code&gt;carbon-relay.py&lt;/code&gt;s.  I have found two instances of &lt;code&gt;carbon-cache.py&lt;/code&gt; per Graphite node to be advantageous on the Rackspace Cloud (YMMV).  Their pickle receivers listen on 2014 for instance &lt;code&gt;a&lt;/code&gt; and 2114 for instance &lt;code&gt;b&lt;/code&gt; on each Graphite node.&lt;/p&gt;

&lt;p&gt;Each &lt;code&gt;carbon-cache.py&lt;/code&gt; instance must have a name unique with respect to all &lt;code&gt;carbon-cache.py&lt;/code&gt; instances on all Graphite nodes.  For example: &lt;code&gt;1.2.3.4:2014:a&lt;/code&gt;, &lt;code&gt;1.2.3.4:2114:b&lt;/code&gt;, &lt;code&gt;5.6.7.8:2014:c&lt;/code&gt;, &lt;code&gt;5.6.7.8:2114:d&lt;/code&gt;.  This isn&amp;#8217;t obvious from the documentation or even from reading the code, so keep this in mind.&lt;/p&gt;

&lt;h2&gt;Whisper databases&lt;/h2&gt;

&lt;p&gt;I started the migration from one Graphite node to two by &lt;code&gt;rsync&lt;/code&gt;ing all Betable&amp;#8217;s Whisper databases from the old node to the new node.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rsync -avz --exclude=carbon graphite-ops.betable.com:/var/lib/graphite/whisper /var/lib/graphite/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The original plan (which is executed below) was to go back later and &amp;#8220;garbage collect&amp;#8221; the Whisper databases that didn&amp;#8217;t belong after the Graphite cluster was up-and-running.  If you&amp;#8217;re feeling adventurous, you should use &lt;code&gt;whisper-clean.py&lt;/code&gt; from &lt;a href="https://gist.github.com/3153844"&gt;https://gist.github.com/3153844&lt;/a&gt; to choose which Whisper databases to &lt;code&gt;rsync&lt;/code&gt; and delete up-front because a Whisper database on the local filesystem will be preferred over querying a remote &lt;code&gt;carbon-cache&lt;/code&gt; instance.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;carbon-relay.py&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Each Graphite node should run a single &lt;code&gt;carbon-relay.py&lt;/code&gt; instance.  It should listen on 2003 and 2004 so your senders don't have to be reconfigured.&lt;/p&gt;

&lt;p&gt;List all &lt;code&gt;carbon-cache.py&lt;/code&gt; instances on all Graphite nodes in &lt;code&gt;DESTINATIONS&lt;/code&gt; in &lt;code&gt;carbon.conf&lt;/code&gt;, each with its &lt;code&gt;PICKLE_RECEIVER_INTERFACE&lt;/code&gt;, &lt;code&gt;PICKLE_RECEIVER_PORT&lt;/code&gt;, and instance name.  Sort the instances by their name.  This is what allows metrics to arrive at any Graphite node and be routed to the right Whisper database.&lt;/p&gt;

&lt;p&gt;To use consistent hashing or not to use consistent hashing?  That&amp;#8217;s your own problem.  I use consistent hashing because I have better things to do than balance a Graphite cluster by hand.&lt;/p&gt;

&lt;p&gt;With this configuration, Graphite&amp;#8217;s write path is federated between two nodes.  Its read path, however, appears to be missing half its data.&lt;/p&gt;

&lt;h2&gt;Web app&lt;/h2&gt;

&lt;p&gt;Each Graphite node should run the web app, even if you don&amp;#8217;t plan to use it directly.&lt;/p&gt;

&lt;p&gt;Each web app should list the &lt;strong&gt;local&lt;/strong&gt; &lt;code&gt;carbon-cache.py&lt;/code&gt; instances in &lt;code&gt;CARBONLINK_HOSTS&lt;/code&gt; each with its &lt;code&gt;CACHE_QUERY_INTERFACE&lt;/code&gt;, &lt;code&gt;CACHE_QUERY_PORT&lt;/code&gt;, and instance name.  Sort the instances by their name as before so the consistent hash ring works properly.  If you&amp;#8217;re not using consistent hashing, sort the instances by their name to appease my OCD.&lt;/p&gt;

&lt;p&gt;Each web app should list the other web apps in &lt;code&gt;CLUSTER_SERVERS&lt;/code&gt;, each with its address and port.  If a web app lists itself in &lt;code&gt;CLUSTER_SERVERS&lt;/code&gt;, it&amp;#8217;s gonna have a bad time.&lt;/p&gt;

&lt;h2&gt;Garbage collection&lt;/h2&gt;

&lt;p&gt;Once you&amp;#8217;re satisfied with your Graphite cluster, it&amp;#8217;s time to collect the garbage left by &lt;code&gt;rsync&lt;/code&gt;ing all those Whisper files around.  &lt;code&gt;whisper-clean.py&lt;/code&gt; from &lt;a href="https://gist.github.com/3153844"&gt;https://gist.github.com/3153844&lt;/a&gt; does exactly that.&lt;/p&gt;

&lt;p&gt;(Of course, the usual disclaimers about how this deletes data but is only known to work for me apply so tread lightly.)&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;1.2.3.4&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DJANGO_SETTINGS_MODULE="graphite.settings" python whisper-clean.py 1.2.3.4:a 1.2.3.4:b -5.6.7.8:c -5.6.7.8:d&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On &lt;code&gt;5.6.7.8&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DJANGO_SETTINGS_MODULE="graphite.settings" python whisper-clean.py -1.2.3.4:a -1.2.3.4:b 5.6.7.8:c 5.6.7.8:d&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you happen to have been smarter than I was with your &lt;code&gt;rsync&lt;/code&gt;ing, this step probably won&amp;#8217;t be necessary.&lt;/p&gt;

&lt;h2&gt;Configuration management&lt;/h2&gt;

&lt;p&gt;None of this configuration is done by hand.  We use Puppet but Chef works just fine.  I highly recommend using &lt;a href="http://docs.puppetlabs.com/guides/exported_resources.html"&gt;Exported Resources&lt;/a&gt;, &lt;a href="https://github.com/rcrowley/puppet-related_nodes"&gt;&lt;code&gt;puppet-related_nodes&lt;/code&gt;&lt;/a&gt;, or &lt;a href="http://wiki.opscode.com/display/chef/Search"&gt;Chef Search&lt;/a&gt; to keep the Graphite cluster aware of itself.&lt;/p&gt;

&lt;p&gt;Betable&amp;#8217;s Graphite cluster learns its topology from &lt;code&gt;related_nodes&lt;/code&gt; queries like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$carbonlink_hosts = related_nodes(Graphite::Carbon::Cache, true)
$cluster_servers = related_nodes(Package["graphite"])
$destinations = related_nodes(Graphite::Carbon::Cache, true)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;that pick up Graphite nodes and the &lt;code&gt;graphite::carbon::cache&lt;/code&gt; resources declared with each &lt;code&gt;node&lt;/code&gt; stanza.  These enforce that instance names (&lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;, &lt;code&gt;d&lt;/code&gt;, etc.) are unique across the cluster.  Each &lt;code&gt;graphite::carbon::cache&lt;/code&gt; resource generates a SysV-init script and a &lt;code&gt;service&lt;/code&gt; resource that runs a &lt;code&gt;carbon-cache.py&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;carbon.conf&lt;/code&gt; and &lt;code&gt;local_settings.py&lt;/code&gt; templates Puppet uses are in &lt;a href="https://gist.github.com/3248921"&gt;https://gist.github.com/3248921&lt;/a&gt;.&lt;/p&gt;
&lt;/article&gt;

</atom:content></atom:entry></atom:feed>
