<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/scalability/</id>
  <link href="https://www.endpointdev.com/blog/tags/scalability/"/>
  <link href="https://www.endpointdev.com/blog/tags/scalability/" rel="self"/>
  <updated>2019-03-18T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Extensible Binary Encoding with CBOR</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/03/extensible-binary-encoding-with-cbor/"/>
      <id>https://www.endpointdev.com/blog/2019/03/extensible-binary-encoding-with-cbor/</id>
      <published>2019-03-18T00:00:00+00:00</published>
      <author>
        <name>Matt Vollrath</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2019/03/extensible-binary-encoding-with-cbor/convert-crop.png&#34; alt=&#34;illustration of man converting something in a machine&#34; /&gt;
&lt;p&gt;CBOR is a relatively new IETF draft standard extensible binary data format. Compared to similar formats like MessagePack and BSON, CBOR was developed from the ground up with clear goals:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;unambiguous encoding of most common data formats from Internet standards&lt;/li&gt;
&lt;li&gt;code compactness for encoder and decoder&lt;/li&gt;
&lt;li&gt;no schema description needed&lt;/li&gt;
&lt;li&gt;reasonably compact serialization&lt;/li&gt;
&lt;li&gt;applicability to constrained and unconstrained applications&lt;/li&gt;
&lt;li&gt;good JSON conversion&lt;/li&gt;
&lt;li&gt;extensibility&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;—&lt;a href=&#34;https://tools.ietf.org/html/rfc7049&#34;&gt;RFC 7049&lt;/a&gt; Appendix E, Copyright © 2013 IETF Trust, Bormann &amp;amp; Hoffman&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In the context of data storage and messaging, most developers can relate to CBOR as a binary drop-in replacement for JSON. While CBOR doesn’t share the human readability of JSON, it can efficiently and unambiguously encode types of data that JSON struggles with. CBOR can also be extended with tags to optimize serialization beyond its standard primitives.&lt;/p&gt;
&lt;h3 id=&#34;encoding-binary-data&#34;&gt;Encoding Binary Data&lt;/h3&gt;
&lt;p&gt;JSON is a ubiquitous data format for web and beyond, for many good reasons, but encoding blobs of binary data is an area where JSON falters. For example, if you are designing a JSON protocol to wrap the storage or transfer of arbitrary objects, your options are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Require that all input data can be represented as JSON. When possible this is potentially a reasonable solution, but limits the types of data that can be encoded. Notable exceptions include most popular image encodings, excluding SVG.&lt;/li&gt;
&lt;li&gt;Base64 encode any binary data values to a string. This can encode any binary data, but increases the size of the data by a minimum of 1/3, incurs encoding and decoding cost, and requires magic to indicate that the string is Base64 encoded.&lt;/li&gt;
&lt;li&gt;Encode the bytes as an array of numbers or a hex string. These are probably not things you should do, but it seemed worth mentioning that these techniques increase the size of the data by anywhere from 2x to 5x and also require magic to indicate that the data is really binary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With CBOR, binary blobs of any length are supported out of the box and are encoded 1:1. Encoding and decoding CBOR byte strings is extremely fast, even in higher-level languages. In the case of JavaScript, CBOR byte strings can be transcoded to and from the very fast and efficient &lt;code&gt;Uint8Array&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, let’s try encoding a simple object with two fields: “name” and “data”. An abstract view of this object:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;name: &amp;#34;Strawberry Pie&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;data: &amp;lt;00 01 02 03 04 05 06 07 08 09&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A C struct of this raw data would be absolute minimum of 24 bytes.&lt;/p&gt;
&lt;p&gt;If you Base64 the data into JSON, your output might look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Strawberry Pie&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;AAECAwQFBgcICQ==&amp;#34;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 51 bytes.&lt;/p&gt;
&lt;p&gt;This illustrates the “magic” problem with Base64 encoding binary data in JSON. Unless you have a JSON schema or special protocol fields, the decoder has no indication that the data in this object needs to be Base64 decoded, because it looks like a string. It is not self-describing.&lt;/p&gt;
&lt;p&gt;The CBOR representation of the same input data, as a hex string:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a2646e616d656e5374726177626572727920506965696a7065675f646174614a00010203040506070809&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whoa, there. What do all these numbers and letters mean? I miss my JSON! The Node &lt;a href=&#34;https://www.npmjs.com/package/cbor&#34;&gt;cbor&lt;/a&gt; package has a handy &lt;a href=&#34;https://github.com/hildjj/node-cbor/blob/master/bin/cbor2comment&#34;&gt;&lt;code&gt;cbor2comment&lt;/code&gt; tool&lt;/a&gt; to annotate this hex string for us.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  a2                -- Map, 2 pairs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    64              -- String, length: 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      6e616d65      -- {Key:0}, &amp;#34;name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    6e              -- String, length: 14
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      5374726177626572727920506965 -- {Val:0}, &amp;#34;Strawberry Pie&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    64              -- String, length: 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      64617461 -- {Key:1}, &amp;#34;data&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    4a              -- Bytes, length: 10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      00010203040506070809 -- {Val:1}, 00010203040506070809&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 35 bytes.&lt;/p&gt;
&lt;p&gt;Now let’s &lt;a href=&#34;https://github.com/mvollrath/cbor-bench&#34;&gt;benchmark JSON against CBOR using real JPEG data&lt;/a&gt;. For this test I used a &lt;a href=&#34;https://github.com/mvollrath/cbor-js/tree/fast_byte_array_encoding&#34;&gt;modified version of cbor-js&lt;/a&gt;, a library compatible with both Node and browsers, and encoded a 910,226 byte JPEG of strawberry rhubarb pie.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                           | JSON      | CBOR    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| :------------------------ | --------: | ------: |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Median encoding time (ms) | 3.983     | 0.538   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Median decoding time (ms) | 3.151     | 0.006   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Encoded size (bytes)      | 1,213,676 | 910,262 |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As the numbers show, CBOR is both faster and more concise for this particular data. Also, CBOR pie tastes better.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/extensible-binary-encoding-with-cbor/strawberry_pie.jpg&#34; alt=&#34;strawberry rhubarb pie&#34;&gt;&lt;/p&gt;
&lt;p&gt;Food always looks good in pictures!&lt;/p&gt;
&lt;h3 id=&#34;optimizing-cbor-with-tags&#34;&gt;Optimizing CBOR with Tags&lt;/h3&gt;
&lt;p&gt;In the case of encoding homogeneous numeric arrays, CBOR encoders can struggle with optimizing the packing of the data. For example, if you have an array of floating point numbers in a higher-level language like Python or JavaScript, the CBOR encoder implementation won’t necessarily determine how many bits are required to encode the numbers, defaulting to the largest available. Additionally, each value in the array will be individually described as a floating point number. This increases the cost and size of the data considerably.&lt;/p&gt;
&lt;p&gt;CBOR has an answer to this problem. The &lt;a href=&#34;https://datatracker.ietf.org/doc/draft-ietf-cbor-array-tags/?include_text=1&#34;&gt;Draft Typed Array Tags&lt;/a&gt; spec includes tags specifying typed arrays which happen to match JavaScript &lt;code&gt;TypedArray&lt;/code&gt; flavors.&lt;/p&gt;
&lt;p&gt;For example, let’s say you have a &lt;code&gt;Float32Array&lt;/code&gt; with a few values:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; floats = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Float32Array([&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1.234567&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2.345678&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3.456789&lt;/span&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;JSON has a really funny way of encoding a &lt;code&gt;Float32Array&lt;/code&gt;, lookit all those bytes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1.2345670461654663&lt;/span&gt;,&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2.3456780910491943&lt;/span&gt;,&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3.456789016723633&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 69 bytes.&lt;/p&gt;
&lt;p&gt;If we were to help the encoder by sending it a regular &lt;code&gt;Array&lt;/code&gt; it would still be pretty verbose, but the precision we weren’t using is truncated, so the overall length will vary wildly depending on the values in the &lt;code&gt;Array&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[1.234567,2.345678,3.456789]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 28 bytes.&lt;/p&gt;
&lt;p&gt;The well-meaning Node cbor library can encode the &lt;code&gt;Float32Array&lt;/code&gt; directly, but doesn’t try to optimize for size:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  83                -- Array, 3 items
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fb              -- Float, next 8 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      3ff3c0c960000000 -- [0], 1.2345670461654663
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fb              -- Float, next 8 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      4002c3f2e0000000 -- [1], 2.3456780910491943
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fb              -- Float, next 8 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      400ba78100000000 -- [2], 3.456789016723633&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 28 bytes.&lt;/p&gt;
&lt;p&gt;Look at all that wasted precision, 64 bits for each 32-bit float, this just won’t do! The CBOR spec allows you to “tag” data for special treatment. According to the &lt;a href=&#34;https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml&#34;&gt;list of registered CBOR tags&lt;/a&gt;, “IEEE 754 binary32, little endian, Typed Array” is tag 85. The consecutive bytes of the three numbers follow.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  d8                --  next 1 byte
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    55              -- Tag #85
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      4c            -- Bytes, length: 12
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        4b069e3f971f1640083c5d40 -- 4b069e3f971f1640083c5d40&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 15 bytes.&lt;/p&gt;
&lt;p&gt;The contained 12 byte string is equivalent to the values in the ArrayBuffer underneath our Float32Array.&lt;/p&gt;
&lt;p&gt;The CBOR decoder will spit out the tag alongside its associated blob, so our optimization is self-described. Now we need to ensure that a byte string with this tag is correctly converted to the typed array, minding endianness. In a mature JavaScript implementation, this is both easy and very fast. Because we encoded the values of the &lt;code&gt;ArrayBuffer&lt;/code&gt; underneath a &lt;code&gt;Float32Array&lt;/code&gt;, we can construct a new &lt;code&gt;Float32Array&lt;/code&gt; from the &lt;code&gt;ArrayBuffer&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; floats = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Float32Array(bytes);  &lt;span style=&#34;color:#888&#34;&gt;// assuming platform and bytes are same endianness!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a very scalable way to read and write arrays of numeric values into JavaScript.&lt;/p&gt;
&lt;h3 id=&#34;when-to-use-cbor&#34;&gt;When to Use CBOR&lt;/h3&gt;
&lt;p&gt;As &lt;a href=&#34;https://youtu.be/9FBmHB-YoQw?t=57&#34;&gt;King Crimson teaches us&lt;/a&gt;, “it doesn’t mean you should / just because you can.”&lt;/p&gt;
&lt;p&gt;I’ve found CBOR useful as an alternative to JSON for large (&amp;gt;2kB) chunks of raw data or numeric arrays. In many other cases JSON is equivalent or superior, because most languages have native JSON encoding and decoding in their standard libraries. CBOR encoders are not always so optimized, but the gap is closing with wider adoption. Run your own tests and benchmarks on real data against real libraries before deciding to use CBOR.&lt;/p&gt;
&lt;p&gt;Many readers will recognize that schemaful formats such as protocol buffers are an endgame for structured data. If the infrastructure demands make sense for your application, this is also a good way to go. If you’re working with an application that already uses JSON, the difference in development and maintenance costs of porting it to CBOR or protobuf-likes should be measured against the size and performance gains of each approach.&lt;/p&gt;
&lt;p&gt;It’s tempting to let numbers alone decide what format to use, but don’t underestimate the value of JSON’s human readability for structured data. The value of human readability diminishes quickly when dealing with large numeric arrays or binary blobs, so once again CBOR is an appealing choice for these data.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>wroc_love.rb 2017 part 2: The Elixir Hype</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/03/wrocoverb-part-2-elixir-hype/"/>
      <id>https://www.endpointdev.com/blog/2017/03/wrocoverb-part-2-elixir-hype/</id>
      <published>2017-03-21T00:00:00+00:00</published>
      <author>
        <name>Wojtek Ziniewicz</name>
      </author>
      <content type="html">
        &lt;p&gt;One of the main reasons I attend &lt;a href=&#34;https://wrocloverb.com/&#34;&gt;wroc_love.rb&lt;/a&gt; almost every year, is that it’s a great forum for confronting ideas. It’s almost a tradition to have at least 2 very enjoyful discussion panels during this conference. One of them was devoted to &lt;a href=&#34;http://elixir-lang.org/&#34;&gt;Elixir&lt;/a&gt; and why the Ruby [1] community is so hyping about it.&lt;/p&gt;
&lt;h4 id=&#34;why-elixir-is-sold-to-us-as-new-better-ruby-while-its-underlying-principles-are-totally-different-wont-it-result-in-elixir-programmers-that-do-not-understand-elixir-like-rails-programmers-that-do-not-know-ruby&#34;&gt;Why Elixir is “sold” to us as “new better Ruby” while its underlying principles are totally different? Won’t it result in Elixir programmers that do not understand Elixir (like Rails programmers that do not know Ruby)?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Panelists discussed briefly the history of Elixir:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Jose Valim (who created Elixir) was working on threading in Rails and he was searching for better approaches for threading in web frameworks. He felt like lots of things were lacking in Erlang and Elixir is his approach for better Exceptions, better developer experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Then they jumped to Elixir’s main goals which are:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compatibility with Erlang (all datatypes)&lt;/li&gt;
&lt;li&gt;Better tooling&lt;/li&gt;
&lt;li&gt;Improving developers’ experience&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;After that, they started speculating about problems that Elixir solves and RoR doesn’t:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ruby on Rails addresses many problems in ways that may be somehow archaic to us in the ever-​scaling world of 2017. There are many approaches to it, e.g. “actor model” which is implemented in Ruby by Celluloid, in Scala with Akka and also Elixir and Phoenix (Elixir’s rails-like framework) has its own actor model.&lt;/p&gt;
&lt;p&gt;Phoenix (“Rails for Elixir”) is just an Elixir app—​unlike Rails, it is not separate from Elixir. Moreover Elixir is exactly the same language as Erlang so:&lt;/p&gt;
&lt;p&gt;Erlang = Elixir = Phoenix&lt;/p&gt;
&lt;p&gt;Great comment:&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34; data-lang=&#34;en&#34;&gt;
&lt;div dir=&#34;ltr&#34; lang=&#34;en&#34;&gt;
Elixir is same as Erlang but not really &lt;a href=&#34;https://twitter.com/hashtag/wrocloverb?src=hash&#34;&gt;#wrocloverb&lt;/a&gt;&lt;/div&gt;
— Daria Woznicka (@DWoznicka) &lt;a href=&#34;https://twitter.com/DWoznicka/status/843102967339384832&#34;&gt;March 18, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Then a discussion followed during which panelists speculated about the price of the jump from Rails to Elixir:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Java to Rails jump was caused by business/​productivity. There’s no such jump for Phoenix/​Elixir. Elixir code is more verbose (less instance variables, all args are passed explicitly to all functions).&lt;/p&gt;
&lt;h3 id=&#34;my-conclusions&#34;&gt;My conclusions&lt;/h3&gt;
&lt;p&gt;A reason why this discussion was somehow shallow and pointless was that those two worlds have different problems. Great comment:&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34; data-lang=&#34;en&#34;&gt;
&lt;div dir=&#34;ltr&#34; lang=&#34;en&#34;&gt;
Ruby guys focus on business. Elixir guys on technical aspects &lt;a href=&#34;https://twitter.com/hashtag/wrocloverb?src=hash&#34;&gt;#wrocloverb&lt;/a&gt;&lt;/div&gt;
— Michał Łomnicki (@mlomnicki) &lt;a href=&#34;https://twitter.com/mlomnicki/status/843106473358049280&#34;&gt;March 18, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;Elixir solves a lot of technical problems with scaling thanks to &lt;a href=&#34;https://www.erlang.org/&#34;&gt;Erlang’s&lt;/a&gt; virtual machine. Such problems are definitely only a small part of what typical Ruby problem-​solvers deal with on a daily basis. Hearing Elixir and Ruby on Rails developers talk past each other was probably the first sign of the fact that there’s no hyping technology right now. Each problem can be addressed by many tech tools and frameworks.&lt;/p&gt;
&lt;p&gt;[1] Wrocloverb describes itself as a “best Java conference in Ruby world”. It’s deceiving:&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34; data-lang=&#34;en&#34;&gt;
&lt;div dir=&#34;ltr&#34; lang=&#34;en&#34;&gt;
&lt;a href=&#34;https://twitter.com/hashtag/wrocloverb?src=hash&#34;&gt;#wrocloverb&lt;/a&gt; was great Clojure conference :)&lt;br /&gt;
Thanks for all organizers and speakers!&lt;/div&gt;
— Jakub Cieślar (@jcieslar_) &lt;a href=&#34;https://twitter.com/jcieslar_/status/843596752926269443&#34;&gt;March 19, 2017&lt;/a&gt;&lt;/blockquote&gt;

      </content>
    </entry>
  
    <entry>
      <title>Pgbouncer user and database pool_mode with Scaleway</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/09/pgbouncer-user-and-database-poolmode/"/>
      <id>https://www.endpointdev.com/blog/2015/09/pgbouncer-user-and-database-poolmode/</id>
      <published>2015-09-24T00:00:00+00:00</published>
      <author>
        <name>Greg Sabino Mullane</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; float: right; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/09/pgbouncer-user-and-database-poolmode/image-0.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;333&#34; data-original-width=&#34;500&#34; id=&#34;jA0ECgMCcw4XQprbL4Fg0nYB3a6dd1Vi67QwWMs7TvxhnNfGQ2Qze6lTuPNBjKVpfppBeYnI8F9EumHNXWq9EfoO2spSKn/O4L7ls+X8VcOivVg5IRIFusCv9P8fSftPW/Bvkvp/PnC344QZDFpZ0nxEVcqJ0JGnoKGW3em3InqwzIWYXNT7=aYud&#34; src=&#34;/blog/2015/09/pgbouncer-user-and-database-poolmode/image-0.jpeg&#34; width=&#34;400&#34;/&gt;&lt;/a&gt;&lt;br/&gt;&lt;small&gt;(&lt;a href=&#34;https://flic.kr/p/nUKQby&#34;&gt;Waterfall photo&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/photos/lukeprice88/&#34;&gt;Luke Price&lt;/a&gt;&lt;/small&gt;)&lt;/div&gt;
&lt;p&gt;The recent release of &lt;a href=&#34;https://pgbouncer.github.io/&#34;&gt;PgBouncer 1.6&lt;/a&gt;, a connection
pooler for Postgres, brought a number of new features. The two I want to demonstrate today are
the per-database and per-use pool_modes. To get this effect
previously, one had to run separate instances of PgBouncer.
As we shall see, a single instance can now run different pool_modes seamlessly.&lt;/p&gt;
&lt;p&gt;There are three pool modes available in PgBouncer, representing how aggressive
the pooling becomes: &lt;strong&gt;session mode&lt;/strong&gt;, &lt;strong&gt;transaction mode&lt;/strong&gt;, and &lt;strong&gt;statement mode&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Session pool mode&lt;/strong&gt; is the default, and simply allows you to avoid
the startup costs for new connections. PgBouncer connects to Postgres, keeps the
connection open, and hands it off to clients connecting to PgBouncer. This
handoff is faster than connecting to Postgres itself, as no new backends need to
be spawned. However, it offers no other benefits, and many clients/applications
already do their own connection pooling, making this the least useful pool mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Transaction pool mode&lt;/strong&gt; is probably the most useful one. It works by
keeping a client attached to the same Postgres backend for the duration of
a transaction only. In this way, many clients can share the same backend
connection, making it possible for Postgres to handle a large number of clients
with a small &lt;strong&gt;max_connections&lt;/strong&gt; setting (each of which consumes resources).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Statement pool mode&lt;/strong&gt; is the most interesting one, as it makes no
promises at all to the client about maintaining the same Postgres backend. In
other words, every time a client runs a SQL statement, PgBouncer releases that
connections back to the pool. This can make for some great performance gains,
although it has drawbacks, the primary one being no multi-statement transactions.&lt;/p&gt;
&lt;p&gt;To demonstrate the new pool_mode features, I decided to try out a new service
mentioned by a coworker, called &lt;a href=&#34;https://www.scaleway.com/&#34;&gt;Scaleway&lt;/a&gt;. Like Amazon Web Services (AWS), it
offers quick-to-create cloud servers, ideal for testing and demonstrating.
The unique things about Scaleway is the servers are all ARM-based SSDs.
Mini-review of Scaleway: I liked it a lot. The interface was smooth and
uncluttered (looking askance at you, AWS), the setup was very fast, and I had
no problems with it being ARM.&lt;/p&gt;
&lt;p&gt;To start a new server (once I entered my billing information, and pasted
my public SSH key in), I simply clicked the create server button, chose
“Debian Jessie (8.1)”, and then create server again. 60 seconds later,
I had an IP address to login as root. The first order of business, as always,
is to make sure things are up to date and install some important tools:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# apt-get upgrade
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Only five packages were upgraded, which means things are already very up to date
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Because just plain &amp;#39;apt-get&amp;#39; only gets you so far:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# apt-get install aptitude
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## To find out the exact names of some critical packages:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# aptitude search emacs git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Because a server without emacs is like a jungle without trees:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# apt-get install emacs-nox git-core
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## To figure out what version of Postgres is available:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# aptitude show postgresql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Package: postgresql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;State: not installed
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Version: 9.4+165
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Since 9.4 is the latest, we will happily use it for this demo:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# apt-get install postgresql postgresql-contrib
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Nice to not have to worry about initdb anymore:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# service postgresql start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Postgres 9.4 is now installed, and started up. Time to figure out where
the configuration files are and make a few small changes. We will turn
on some heavy logging via the &lt;strong&gt;postgresql.conf&lt;/strong&gt; file, and allow anyone
locally to log in to Postgres, no questions asked, by changing
the &lt;strong&gt;pg_hba.conf&lt;/strong&gt; file. Then we restart Postgres, and verify it is working:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# updatedb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# locate postgresql.conf pg_hba.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# echo &amp;#34;logging_collector = on
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log_filename = &amp;#39;postgres-%Y-%m-%d.log&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log_rotation_size = 0&amp;#34; &amp;gt;&amp;gt; /etc/postgresql/9.4/main/postgresql.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## But it already has a nice log_line_prefix (bully for you, Debian)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Take a second to squirrel away the old version before overwriting:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# cp /etc/postgresql/9.4/main/pg_hba.conf ~
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# echo &amp;#34;local all all trust&amp;#34; &amp;gt; /etc/postgresql/9.4/main/pg_hba.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# service postgresql restart
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# psql -U postgres -l
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                             List of databases
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   Name    |  Owner   | Encoding  | Collate | Ctype |   Access privileges
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------+----------+-----------+---------+-------+-----------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; postgres  | postgres | SQL_ASCII | C       | C     |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; template0 | postgres | SQL_ASCII | C       | C     | =c/postgres          +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           |          |           |         |       | postgres=CTc/postgres
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; template1 | postgres | SQL_ASCII | C       | C     | =c/postgres          +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           |          |           |         |       | postgres=CTc/postgres
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(3 rows)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;SQL_ASCII? Yuck, how did that get in there?! That’s an absolutely terrible
encoding to be using in 2015, so we need to change that right away. Even
though it won’t affect this demonstration, there is the principle of the matter.
We will create a new database with a sane encoding, then create some test databases
based on that.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# su - postgres
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~$ createdb -T template0 -E UTF8 -l en_US.utf8 foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~$ for i in {1..5}; do createdb -T foo test$i; done
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~$ psql -l
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                             List of databases
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   Name    |  Owner   | Encoding  |  Collate   |   Ctype    |   Access privileges
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------+----------+-----------+------------+------------+-----------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; foo       | postgres | UTF8      | en_US.utf8 | en_US.utf8 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; postgres  | postgres | SQL_ASCII | C          | C          |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; template0 | postgres | SQL_ASCII | C          | C          | =c/postgres          +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           |          |           |            |            | postgres=CTc/postgres
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; template1 | postgres | SQL_ASCII | C          | C          | =c/postgres          +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           |          |           |            |            | postgres=CTc/postgres
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; test1     | postgres | UTF8      | en_US.utf8 | en_US.utf8 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; test2     | postgres | UTF8      | en_US.utf8 | en_US.utf8 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; test3     | postgres | UTF8      | en_US.utf8 | en_US.utf8 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; test4     | postgres | UTF8      | en_US.utf8 | en_US.utf8 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; test5     | postgres | UTF8      | en_US.utf8 | en_US.utf8 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(9 rows)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Create some test users as well:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~$ for u in {&amp;#39;alice&amp;#39;,&amp;#39;bob&amp;#39;,&amp;#39;eve&amp;#39;,&amp;#39;mallory&amp;#39;}; do createuser $u; done
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## First time I tried this I outfoxed myself - so make sure the users can connect!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~$ for d in {1..5}; do psql test$d -qc &amp;#39;grant all on all tables in schema public to public&amp;#39;; done
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Make sure we can connect as one of our new users:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~$ psql -U alice test1 -tc &amp;#39;show effective_cache_size&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 847283&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that Postgres is up and running, let’s install PgBouncer. Since we are
showing off some 1.6 features, it is unlikely to be available via packaging,
but we will check anyway.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~$ aptitude versions pgbouncer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Package pgbouncer:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;p   1.5.4-6+deb8u1            stable            500
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Not good enough! Let&amp;#39;s grab 1.6.1 from git:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~$ git clone https://github.com/pgbouncer/pgbouncer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~$ cd pgbouncer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## This submodule business for such a small self-contained project really irks me :)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ git submodule update --init
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ git checkout pgbouncer_1_6_1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ ./autogen.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The autogen.sh script fails rather quickly with an error about libtool—​which is to be expected,
as PgBouncer comes with a small list of required packages in order to build it. Because monkeying
around with all those prerequisites can get tiresome, apt-get provides an option called “build-dep”
that (in theory!) allows you to download everything needed to build a specific package. Before doing
that, let’s drop back to root and give the postgres user full sudo permission, so we don’t have to
keep jumping back and forth between accounts:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ exit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## This is a disposable test box - do not try this at home!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Debian&amp;#39;s /etc/sudoers has #includedir /etc/sudoers.d, so we can do this:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# echo &amp;#34;postgres ALL= NOPASSWD:ALL&amp;#34; &amp;gt; /etc/sudoers.d/postgres
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@scw-56578065:~# su - postgres
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~ cd postgres
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ sudo apt-get build-dep pgbouncer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;The following NEW packages will be installed:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  asciidoc autotools-dev binutils build-essential cdbs cpp cpp-4.9 debhelper docbook-xml docbook-xsl dpkg-dev g++ g++-4.9 gcc gcc-4.9 gettext
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  gettext-base intltool-debian libasan1 libasprintf0c2 libatomic1 libc-dev-bin libc6-dev libcloog-isl4 libcroco3 libdpkg-perl libevent-core-2.0-5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  libevent-dev libevent-extra-2.0-5 libevent-openssl-2.0-5 libevent-pthreads-2.0-5 libgcc-4.9-dev libgomp1 libisl10 libmpc3 libmpfr4 libstdc++-4.9-dev
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  libubsan0 libunistring0 libxml2-utils linux-libc-dev po-debconf sgml-data xmlto xsltproc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ ./autogen.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whoops, another build failure. Well, build-dep isn’t perfect, turns out we still need
a few packages. Let’s get this built, create some needed directories, tweak permissions,
find the location of the installed PgBouncer ini file, and make a few changes to it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ sudo apt-get install libtools automake pkg-config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ ./autogen.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ ./configure
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ make
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ sudo make install
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ sudo updatedb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ locate pgbouncer.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/var/lib/postgresql/pgbouncer/etc/pgbouncer.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ sudo mkdir /var/log/pgbouncer /var/run/pgbouncer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ sudo chown postgres.postgres /var/log/pgbouncer \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; /var/run/pgbouncer /var/lib/postgresql/pgbouncer/etc/pgbouncer.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ emacs /var/lib/postgresql/pgbouncer/etc/pgbouncer.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Add directly under the [databases] section:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test1 = dbname=test1 host=/var/run/postgresql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test2 = dbname=test2 host=/var/run/postgresql pool_mode=transaction
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test3 = dbname=test3 host=/var/run/postgresql pool_mode=statement
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test4  = dbname=test3 host=/var/run/postgresql pool_mode=statement auth_user=postgres
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Change listen_port to 5432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Comment out listen_addr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Make sure unix_socket_dir is /tmp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;How are we able to use 5432, when Postgres is using it too? In the unix world, a port relies on a
socket file, which is located somewhere on the file system. Thus, you can use the same port as long
as they are coming from different files. While Postgres has a default &lt;strong&gt;unix_socket_directories&lt;/strong&gt; value
of &lt;strong&gt;&amp;rsquo;/tmp&amp;rsquo;&lt;/strong&gt;, Debian has changed that to &lt;strong&gt;&amp;rsquo;/var/run/postgresql&amp;rsquo;&lt;/strong&gt;, meaning PgBouncer itself is
free to use &lt;strong&gt;&amp;rsquo;/tmp&amp;rsquo;&lt;/strong&gt;!
The bottom line is that we can use port 5432 for both Postgres and PgBouncer, and control which one
is used by setting the host parameter when connecting (which, when starting with a slash, is
actually the location of the socket file). However, note that only one of them can be used when
connecting via TCP/IP. Enough of all that, let’s make sure PgBouncer at least
starts up!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ pgbouncer /var/lib/postgresql/pgbouncer/etc/pgbouncer.ini -d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2000-08-04 02:06:08.371 5555 LOG File descriptor limit: 65536 (H:65536),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; max_client_conn: 100, max fds possible: 230
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As expected, the pgbouncer program gave us a single line of information before going into background daemon mode, per
the &lt;strong&gt;-d&lt;/strong&gt; argument. Since both Postgres and PgBouncer are running on port 5432, let’s make our psql prompt
a little more informative, by having it list the hostname via &lt;strong&gt;%M&lt;/strong&gt;. If the hostname matches the unix_socket_directory
value that psql was compiled with, then it will simply show &lt;strong&gt;&amp;rsquo;[local]&amp;rsquo;&lt;/strong&gt;. Thus, seeing &lt;strong&gt;&amp;rsquo;/tmp&amp;rsquo;&lt;/strong&gt; indicates we are
connected to PgBouncer, and seeing &lt;strong&gt;&amp;rsquo;[local]&amp;rsquo;&lt;/strong&gt; indicates we are connected to Postgres (via /var/run/postgresql).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Visit the &amp;lt;a href=&amp;#34;http://www.postgresql.org/docs/current/static/app-psql.html#APP-PSQL-PROMPTING&amp;#34;&amp;gt;psql docs&amp;lt;/a&amp;gt; for explanation of this prompt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ echo &amp;#34;\set PROMPT1 &amp;#39;%n@%/:%&amp;gt;%R%x%#%M &amp;#39;&amp;#34; &amp;gt; ~/.psqlrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s confirm that each PgBouncer connection is in the expected mode. Database test1 should be using the
default pool_mode, “session”. Database test2 should be using a “transaction” pool_mode, while “statement” mode
should be used by both test3 and test4. See my previous blog post on
&lt;a href=&#34;/blog/2015/05/connected-to-pgbouncer-or-postgres/&#34;&gt;ways to detect the various pool_modes of pgbouncer&lt;/a&gt;. First, let’s connect to normal Postgres and
verify we are not connected to PgBouncer by trying to change to a non-existent database. &lt;strong&gt;FATAL&lt;/strong&gt; means
PgBouncer, and &lt;strong&gt;ERROR&lt;/strong&gt; means Postgres:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql test1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql (9.4.3)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Type &amp;#34;help&amp;#34; for help.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test1:5432=#[local] \c crowdiegocrow
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FATAL:  database &amp;#34;crowdiegocrow&amp;#34; does not exist
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql -h /tmp test1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql (9.4.3)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Type &amp;#34;help&amp;#34; for help.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test1:5432=#[local:/tmp] \c sewdiegosew
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  No such database: sewdiegosew&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let’s confirm that we have database-specific pool modes working. If you recall from above, test2 is set to transaction mode,
and test3 is set to statement mode. We determine the mode by running three tests. First, we do a “BEGIN; ROLLBACK;”—​if this fails,
it means we are in statement mode. Next, we try to PREPARE and EXECUTE a statement. If this fails, it means
we are in a transaction mode. Finally, we try to switch to a non-existent database. If it returns an ERROR, it
means we are in session mode. If it returns a FATAL, it means we are not connected to PgBouncer at all.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; float:right; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/09/pgbouncer-user-and-database-poolmode/image-1-big.png&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img alt=&#34;Your mnemonic helper&#34; border=&#34;0&#34; src=&#34;/blog/2015/09/pgbouncer-user-and-database-poolmode/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Mnemonic for this common set of psql options: &amp;#34;axe cutie&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql -Ax -qt -h /tmp test1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test1:5432=#[local:/tmp] BEGIN; ROLLBACK;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test1:5432=#[local:/tmp] PREPARE abc(int) AS SELECT $1::text;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test1:5432=#[local:/tmp] EXECUTE abc(123);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;text|123
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test1:5432=#[local:/tmp] \c rowdiegorow
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  No such database: rowdiegorow
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## test1 is thus running in session pool_mode
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql -Ax -qt -h /tmp test2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test2:5432=#[local:/tmp] BEGIN; ROLLBACK;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test2:5432=#[local:/tmp] PREPARE abc(int) AS SELECT $1::text;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test2:5432=#[local:/tmp] EXECUTE abc(123);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  prepared statement &amp;#34;abc&amp;#34; does not exist
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## test2 is thus running in transaction pool_mode
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql -Ax -qt -h /tmp test3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@test3:5432=#[local:/tmp] BEGIN; ROLLBACK;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  Long transactions not allowed
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## test3 is thus running in statement pool_mode&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So the database-level pool modes are working as expected. PgBouncer now supports user-level
pool modes as well, and these always trump the database-level pool
modes. Recall that our setting for test4 in the pgbouncer.ini file was:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test4 = dbname=test3 host=/var/run/postgresql pool_mode=statement auth_user=postgres&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The addition of the auth_user parameter allows us to specify other users to connect as, without
having to worry about adding them to the PgBouncer auth_file. We added four sample regular users
above: Alice, Bob, Eve, and Mallory. We should only be able to connect with them via auth_user,
so only test4 should work:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql -h /tmp test1 -U alice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql: ERROR:  No such user: alice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql -h /tmp test2 -U alice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql: ERROR:  No such user: alice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql -h /tmp test3 -U alice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql: ERROR:  No such user: alice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql -h /tmp test4 -U alice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql (9.4.3)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Type &amp;#34;help&amp;#34; for help.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;alice@test4:5432=&amp;gt;[local:/tmp] begin;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  Long transactions not allowed&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s see if we can change the pool_mode for Alice to transaction, even if we
are connected to test4 (which is set to statement mode). All it takes is a quick entry
to the &lt;strong&gt;pgbouncer.ini&lt;/strong&gt; file, in a section we must create called &lt;strong&gt;[users]&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &amp;#34;[users]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;alice = pool_mode=transaction
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$(cat /var/lib/postgresql/pgbouncer/etc/pgbouncer.ini)&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; /var/lib/postgresql/pgbouncer/etc/pgbouncer.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Attempt to reload pgbouncer:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ kill -HUP `head -1 /run/pgbouncer/pgbouncer.pid`
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Failed, due to a PgBouncer bug:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2001-03-05 02:27:44.376 5555 FATAL @src/objects.c:299 in function
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  put_in_order(): put_in_order: found existing elem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Restart it:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ pgbouncer /var/lib/postgresql/pgbouncer/etc/pgbouncer.ini -d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;postgres@scw-56578065:~/pgbouncer$ psql -Ax -qt -h /tmp test4 -U alice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;alice@test4:5432=&amp;gt;[local:/tmp] BEGIN; ROLLBACK;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;alice@test4:5432=&amp;gt;[local:/tmp] PREPARE abc(int) AS SELECT $1::text;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;alice@test4:5432=&amp;gt;[local:/tmp] EXECUTE abc(123);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  prepared statement &amp;#34;abc&amp;#34; does not exist
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## test4 is thus running in transaction pool_mode due to the [users] setting&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There you have it—​database-specific and user-specific PgBouncer pool_modes. Note that you cannot
yet do user &lt;em&gt;and&lt;/em&gt; database specific pool_modes, such as if you want Alice to use transaction
mode for database test4 and statement mode for test5.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Connected to PgBouncer or Postgres?</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/05/connected-to-pgbouncer-or-postgres/"/>
      <id>https://www.endpointdev.com/blog/2015/05/connected-to-pgbouncer-or-postgres/</id>
      <published>2015-05-18T00:00:00+00:00</published>
      <author>
        <name>Greg Sabino Mullane</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center; float: right&#34;&gt;&lt;a href=&#34;/blog/2015/05/connected-to-pgbouncer-or-postgres/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;280&#34; data-original-width=&#34;320&#34; src=&#34;/blog/2015/05/connected-to-pgbouncer-or-postgres/image-0.png&#34;/&gt;&lt;/a&gt;&lt;br/&gt;&lt;small&gt;&lt;a href=&#34;https://flic.kr/p/9X5Z89&#34;&gt;Image&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/people/dhwright/&#34;&gt;David Wright&lt;/a&gt;&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;Determining if your current database connection is using
&lt;a href=&#34;https://wiki.postgresql.org/wiki/PgBouncer&#34;&gt;PgBouncer&lt;/a&gt;,
or going directly to Postgres itself, can be challenging, as PgBouncer
is a very low-level, transparent interface. It is possible, and
here are some detection methods you can use.&lt;/p&gt;
&lt;p&gt;This was inspired by someone asking on the Perl DBD IRC
channel if it was possible to easily tell if your current
database handle (usually “$dbh”) is connected to PgBouncer or not.
Since I’ve seen this question asked in other venues, I decided to
take a crack at it.&lt;/p&gt;
&lt;p&gt;There are actually two questions to be answered: (1) are we connected
to PgBouncer, and if so, (2) what pool_mode is being run? The quickest
and easiest way I found to answer the first question is to try and
connect to a non-existent database. Normally, this is a FATAL message,
as seen here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql testdb -p 5432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testdb=# \c ghostdb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FATAL:  database &amp;#34;ghostdb&amp;#34; does not exist
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Previous connection kept
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testdb=#&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, a slightly different ERROR message is returned if the same
thing is attempted while connected to PgBouncer:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql testdb -p 6432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testdb=# \c ghostdb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  No such database: ghostdb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Previous connection kept
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testdb=#&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Thus, an ERROR will always indicate that you are connected to
PgBouncer and not directly to Postgres, which will always issue
a FATAL.&lt;/p&gt;
&lt;p&gt;In the future, there will be an even simpler method. As of this writing,
pgBouncer 1.6 has not been released, but it will have the ability to customize
the &lt;a href=&#34;https://www.postgresql.org/docs/current/static/runtime-config-logging.html#GUC-APPLICATION-NAME&#34;&gt;application_name&lt;/a&gt;. This is a configurable session-level variable that is fairly new in Postgres.
Andrew Dunstan &lt;a href=&#34;http://adpgtech.blogspot.com/2014/05/pgbouncer-enhancements.html&#34;&gt;wrote a patch&lt;/a&gt; which enables adding this to your &lt;strong&gt;pgbouncer.ini&lt;/strong&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;application_name_add_host = 1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will make PgBouncer modify the application_name to append some
information to it such as the remote host, the remote port, and the local port.
This is a feature many PgBouncer users will appreciate,
as it offers an escape from the black hole of connection information
that PgBouncer suffers from. Here is what it looks like on both a normal
Postgres connection, and a PgBouncer connection. As you can see, this is an
easier check than the “invalid database connection” check above:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Postgres:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql testdb -p 5432 -c &amp;#39;show application_name&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; application_name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; psql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## PgBouncer:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql testdb -p 6432 -c &amp;#39;show application_name&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        application_name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; psql - unix(7882@gtsm.com):6432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## DBD::Pg connections to PgBouncer get a very similar change:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ perl testme.tmp.pl --port 6432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app - unix(6772@gtsm.com):6432&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we have answered question of “are we connected to PgBouncer
or not?”. The next question is which pool mode we are in.
There are three pool modes you can set for PgBouncer, which
controls when your particular connection is returned to “the pool”.
For “session” mode, you keep the same Postgres backend the entire time you are
connected. For “transaction”, you keep the same Postgres backend until the end of a
transaction. For “statement”, you may get a new Postgres backend after each statement.&lt;/p&gt;
&lt;p&gt;First, we can check if we are connected to PgBouncer in a statement level
pool mode by taking advantage of the fact that multi-statement transactions
are prohibited. PgBouncer enforces this by intercepting any attempts to
enter a transaction (e.g. by issuing a BEGIN command). A very PgBouncer specific
error about “Long transactions not allowed” is issued back to the client
like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql testdb -p 6432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testdb=# begin;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  Long transactions not allowed&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, that takes care of detecting a pool_mode set to “statement”. The other two modes,
transaction and session, will &lt;em&gt;not&lt;/em&gt; give the same error. Thus, seeing that
error indicates you are using a statement-level PgBouncer connection.&lt;/p&gt;
&lt;p&gt;The next pool mode is “transaction”, which means that the server connection
if released back to the pool at the end of a transaction. To figure out
if we are in this mode, we take advantage of the fact that PgBouncer can
be set to clean up the connection at the end of each transaction by issuing a specific
command. By default, the command set by server_reset_query is
&lt;a href=&#34;https://www.postgresql.org/docs/current/static/sql-discard.html&#34;&gt;DISCARD ALL&lt;/a&gt;, which invalidates
any prepared statements, temporary tables, and other transaction-spanning,
session-level items. Thus, our test will see if these session-level
artifacts get discarded or not:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Direct Postgres:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql testdb -p 5432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testdb=# prepare abc(int) as select $1::text;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PREPARE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testdb=# execute abc(1);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; text
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## PgBouncer:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql testdb -p 6432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testdb=# prepare abc(int) as select $1::text;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PREPARE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testdb=# execute abc(1);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  prepared statement &amp;#34;abc&amp;#34; does not exist&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Keep in mind that there are no true “transactionless” commands in Postgres.
Even though we did not use a BEGIN in the psql prompt above, each command
is treated as its own mini-transaction. In the case of the PgBouncer
connection, the prepare is immediately followed with a DISCARD ALL,
which means that our prepared statement no longer exists. Hence, we
have determined that we  are using a transaction-level
PgBouncer connection.&lt;/p&gt;
&lt;p&gt;Unfortunately, not getting an error does not necessarily mean your
PgBouncer is NOT in transaction mode!  A very negative sentence!
It could be that server_reset_query is empty, meaning that temporary artifacts
are not discarded at the end of the transaction. In such a case, we can
take advantage of the fact that PgBouncer will allow other clients to share
in our current connection, and thus be able to see the temporary items.
If we create a temporary table in one pgbouncer connection, then connect
again as a new client, the temporary table will only show up if we are
sharing sessions but not transactions. Easier shown than explained, I suspect:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Regular Postgres gets a fresh session:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql test1 -p 5432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test1=# create temp table abc(a int);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE TABLE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test1=# select * from abc;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(No rows)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test1=# ^Z ## (we suspend with CTRL-Z)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2]+  Stopped                 psql test1 -p 5432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql test1 -p 5432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test1=# select * from abc;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  relation &amp;#34;abc&amp;#34; does not exist
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## PgBouncer will re-use the same session:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql test1 -p 6432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test1=# create temp table abc(a int);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE TABLE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test1=# select * from abc;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(No rows)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test1=# ^Z
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2]+  Stopped                 psql test1 -p 6432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ psql test1 -p 6432
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test1=# select * from abc;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(No rows)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The final PgBouncer pool mode is “session”, and basically means
the only advantage over a normal Postgres connection is the overhead
to start up and connect to a new Postgres backend. Thus, the
PgBouncer connections are only returned to the pool upon disconnection.
The only way to tell if you are in this mode is by determining that you
are &lt;em&gt;not&lt;/em&gt; in the other two modes. :)&lt;/p&gt;
&lt;p&gt;So, although PgBouncer is extremely transparent, there are some tricks to
determine if you are connected to it, and at what pool_mode. If you
can think of other (SQL-level!) ways to check, please let me know
in the comments section.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Full Page Caching in Interchange 5</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/10/full-page-caching-in-interchange-5/"/>
      <id>https://www.endpointdev.com/blog/2013/10/full-page-caching-in-interchange-5/</id>
      <published>2013-10-28T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently attended the &lt;a href=&#34;http://www.ecommerce-innovation.com/&#34;&gt;eCommerce Innovation Conference 2013&lt;/a&gt; with fellow End Pointer &lt;a href=&#34;/team/richard-templet/&#34;&gt;Richard Templet&lt;/a&gt; and presented on the work End Point has done to develop full-page caching in Interchange 5. The work is in final review currently for inclusion into core Interchange and should provide a roadmap for cache management in Nitesi/Interchange 6.&lt;/p&gt;
&lt;h3 id=&#34;parameters-of-a-caching-solution&#34;&gt;Parameters of a Caching Solution&lt;/h3&gt;
&lt;p&gt;In order to identify the full scope of what one means when one says an application uses caching, there are (at least) 3 aspects to define:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where in the application stack is the cache being applied.&lt;/li&gt;
&lt;li&gt;How long until the cache is expired.&lt;/li&gt;
&lt;li&gt;What level of user state must it support.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The issue of cache duration is not addressed here as any such formulation will be specific to business requirements and tolerance of stale data. I will state, however, that even very brief cache durations can have an enormous impact on performance, particularly for sites that have a relatively small number of resources that absorb the bulk of the traffic. Cache durations of several minutes to several hours can drop the traffic that makes it to Interchange to a small fraction of the overall requests.&lt;/p&gt;
&lt;h3 id=&#34;caching-and-the-application-stack&#34;&gt;Caching and the Application Stack&lt;/h3&gt;
&lt;p&gt;Let’s examine an ideal target architecture for a simple implementation of an Interchange store, or stores. Note that we could also introduce load balancing layers to substantially boost capacity through horizontal scaling at multiple points in this stack, and if we did so we’d need to add those to our list to identify the impact of selecting a target cache point.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Browser&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reverse proxy (e.g., Varnish, Pound, nginx)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Web server (e.g., Apache httpd, nginx)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Interchange, with session storage&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database (e.g., MySQL)&lt;/li&gt;
&lt;li&gt;Other applications (e.g., email server)&lt;/li&gt;
&lt;li&gt;Web services (e.g., payment gateway)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The higher up the stack we can cache, the more scalable our solution becomes. Interchange comes with [timed-build] which can be used to great effect to cache database results in particular, but also potentially other applications that could produce bottlenecks in performance. Moreover, because Interchange assembles the document, this is the last point in the stack that we can (directly, if at all) partially cache a resource. However, having all requests reach Interchange is, itself, a scalability issue that would have to be resolved with either horizontal scaling or pushing our cache farther up the stack.&lt;/p&gt;
&lt;p&gt;It’s also possible to produce a static build of assets in the web server’s doc space, keeping requests from reaching Interchange at all. And while responses from a web server will have considerably less overhead and better response times than Interchange, both building and maintaining a static repository of Interchange assets is going to take some effort and, ultimately, will require horizontal scaling to relieve overload.&lt;/p&gt;
&lt;p&gt;Our target point for the cache described herein is at the reverse proxy. We want to control our cache using standard cache headers and an nginx configuration that uses the full URL for its cache keys. The reverse proxy is chosen because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Very fast and efficient at delivering documents.&lt;/li&gt;
&lt;li&gt;Low maintenance as we are able to have proxy engine keep its data storage fresh according to document headers.&lt;/li&gt;
&lt;li&gt;Assets can now be massively scaled through 3rd-party CDNs requiring no infrastructure investment and maintenance that horizontal scaling solutions lower in the stack would.&lt;/li&gt;
&lt;li&gt;Full URL cache keys and cache headers allow us extend our cache all the way to the browser, for those user agents that respect the cache headers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;core-interchange-support-for-full-page-caching&#34;&gt;Core Interchange Support for Full-Page Caching&lt;/h3&gt;
&lt;p&gt;To provide general support for full-page caching in Interchange, we found it necessary to introduce some core features into Interchange 5. These features are in final review internally and will be committed to core soon.&lt;/p&gt;
&lt;h4 id=&#34;suppresscachedcookies&#34;&gt;SuppressCachedCookies&lt;/h4&gt;
&lt;p&gt;New boolean catalog configuration parameter that, when true, instructs Interchange not to write any cookies in a cacheable response. Cookies are inherently specific to the individual client being served and nginx will refuse to cache a resource containing Set-Cookie headers.&lt;/p&gt;
&lt;h4 id=&#34;instance-volatile&#34;&gt;$::Instance-&amp;gt;{Volatile}&lt;/h4&gt;
&lt;p&gt;Value indicates to critical core code what the request’s cache potential is. Three values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;undef&lt;/strong&gt; - unknown, could be cached, but hasn’t been explicitly identified&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;true&lt;/strong&gt; - cannot be cached. Indicates the requested resource is user-dependent and may produce different results for the same URL for different users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;false&lt;/strong&gt; (other than undef) - explicitly treat as a “can be cached” resource. This setting can be used to reverse override other cache overrides.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The ternary nature of Volatile allows a developer to explicitly control the caching behavior of any given resource if circumstances require an adjustment to the default behavior.&lt;/p&gt;
&lt;h4 id=&#34;if-not-volatile&#34;&gt;[if-not-volatile]&lt;/h4&gt;
&lt;p&gt;New container tag whose body will only interpolate if the value of &lt;code&gt;$::Instance-&amp;gt;{Volatile}&lt;/code&gt; at the time of interpolation is false. Tag is particularly useful for placing settings for cache headers on shared resources (includes files, components, etc.) where the final document may or may not be cacheable.&lt;/p&gt;
&lt;h4 id=&#34;outputcookiehook&#34;&gt;OutputCookieHook&lt;/h4&gt;
&lt;p&gt;Catalog configuration parameter that takes the name of a catalog or global sub to execute just prior to Interchange writing its Set-Cookie headers. Setting was inspired by the need to maintain portions of the session on the client via cookies to allow some more stubborn session-dependent resources to be cacheable.&lt;/p&gt;
&lt;h3 id=&#34;obstacles-to-full-page-caching-in-interchange&#34;&gt;Obstacles to Full-Page Caching in Interchange&lt;/h3&gt;
&lt;p&gt;Standard coding practices in a typical Interchange catalog interfere with full-page caching in a number of ways, primarily:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Liberal coupling of resources with user sessions&lt;/li&gt;
&lt;li&gt;Searches common to all site users (e.g., category lists) generate saved search objects that force session coupling&lt;/li&gt;
&lt;li&gt;Heavy reliance on non-RESTful URLs, primarily those generated by the process actionmap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In most circumstances, these practices can be altered to produce fully cacheable resources, and particularly if the most heavily used components of the site are addressed first, such as the home page and category lists.&lt;/p&gt;
&lt;h3 id=&#34;catalog-changes-to-mitigate-caching-obstacles&#34;&gt;Catalog Changes to Mitigate Caching Obstacles&lt;/h3&gt;
&lt;p&gt;Precisely what changes are required depends on the specific coding practices used for the resources in question. However, there are a number of typical usage patterns that will to some degree affect almost all Interchange catalogs.&lt;/p&gt;
&lt;h4 id=&#34;use-restful-urls&#34;&gt;Use RESTful URLs&lt;/h4&gt;
&lt;p&gt;Avoid unnecessary dependence on the process actionmap, which is often used liberally precisely because it gives lots of hooks into cool and useful features. Avoid any other use of common URLs that produce varying resources based on session dependence.&lt;/p&gt;
&lt;p&gt;Take advantage of writing custom actionmaps, which allow the developer extreme flexibility in URL construction. Because actionmaps make it easy and straightforward to produce unique URLs, they are ideal both for creating cacheable resources and fine-tuning SEO.&lt;/p&gt;
&lt;h4 id=&#34;permanent-more-for-category-lists&#34;&gt;Permanent More for Category Lists&lt;/h4&gt;
&lt;p&gt;By default, search objects which Interchange uses for more lists, are restricted to access from the generating user’s session. This is a safeguard as often search results include personal data for access only to the requestor. However, for features such as category lists, this creates a difficult burden for the developer who wishes to cache the popular resources and whose results are identical across all users.&lt;/p&gt;
&lt;p&gt;We can overcome this difficulty by making the search definitions for category lists, or other canned searches, include the &lt;a href=&#34;/blog/2012/01/interchange-search-caching-with/&#34;&gt;permanent more&lt;/a&gt; indicator. Permanent more causes all identical searches to share a common search object accessible by the same URLs, and freeing the usual coupling with the session of the search originator.&lt;/p&gt;
&lt;h4 id=&#34;address-common-session-variables&#34;&gt;Address Common Session Variables&lt;/h4&gt;
&lt;p&gt;There are certain session variables that are often found in page code and can cause a number of difficulties when trying to make a resource cacheable. Start by tracking through the use of each of the following and come up with a strategy to remove the dependencies so that the interpolated code is free of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[value]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[data session ___]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[set]/[seti]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Any &lt;code&gt;[if]&lt;/code&gt; conditionals that use those respective bases (e.g., &lt;code&gt;[if value]&lt;/code&gt;, &lt;code&gt;[if session]&lt;/code&gt;, and &lt;code&gt;[if scratch]&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;cacheable-redirects&#34;&gt;Cacheable Redirects&lt;/h4&gt;
&lt;p&gt;Any code that issues a redirect must do so consistently with respect to its URL. Any redirect will cache the http code and, if issued conditionally, will force all users accessing the cached resource to also redirect. This practice is seen often in Interchange catalogs, particularly when monitoring pages that are restricted to logged-in users. In summary, it’s OK to cache redirects, but just make sure that a given URL is either always, or never, redirected.&lt;/p&gt;
&lt;h4 id=&#34;profile-and-click-code&#34;&gt;Profile and “Click” Code&lt;/h4&gt;
&lt;p&gt;It is common practice to define profile and click code in scratch variables. This is particularly true for click code defined with the [button] tag, which while convenient causes the click action to be defined under the hood in scratch space. In order for these event-driven features to work, the resource must compile that code and seed it in the session in anticipation of the user’s next actions. If those resources are cached, those important features are never added to the session as the result of a page load and, so, none of the actions will work.&lt;/p&gt;
&lt;p&gt;All use of &lt;code&gt;[button]&lt;/code&gt; or &lt;code&gt;[set]&lt;/code&gt; to produce click or profile code should be moved into the profile files (typically found in &lt;code&gt;etc/profile.*&lt;/code&gt;). There they are added to the Interchange global configuration at compile time and are thus available to all users without regard to the state of their sessions. This is good practice generally since it is often easy (particularly with &lt;code&gt;[button]&lt;/code&gt;) to have multiple actions map to the same scratch key. When that happens, a user going through the browser back button can get invalid results on actions taken because the click or profile definitions have changed with respect to the anticipated such actions on the current page.&lt;/p&gt;
&lt;h4 id=&#34;set-suppresscachedcookies-to-yes&#34;&gt;Set SuppressCachedCookies to Yes&lt;/h4&gt;
&lt;p&gt;As described earlier, this will tell the Interchange core not to write any cookies if the resource is to be cached.&lt;/p&gt;
&lt;h4 id=&#34;define-cache-control-headers&#34;&gt;Define Cache-Control Headers&lt;/h4&gt;
&lt;p&gt;At any point in the development of the response body, Interchange can be issued a pragma that tells it to treat the response as cacheable. This will interact with the core features described above to ensure that there is no impact on the user session as a result of this request, and put the correct headers in the output stream (as well as keep the cookie headers out).&lt;/p&gt;
&lt;p&gt;Invocation looks something like &lt;code&gt;[tag pragma cache_control]max-age=NNN[/tag]&lt;/code&gt;, where NNN is the number of seconds the cache should persist.&lt;/p&gt;
&lt;h3 id=&#34;impact-on-session-management&#34;&gt;Impact on Session Management&lt;/h3&gt;
&lt;p&gt;Any resource considered cacheable should &lt;em&gt;a priori&lt;/em&gt; have neither impact nor dependence on a session. This must be true if we consider that, once cached, a user will interact with the page—​and expect correct behavior—​without ever touching Interchange. This introduces some new conditions associated with the session:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The initial user request against a cacheable resource will &lt;em&gt;not&lt;/em&gt; generate a session. Why is this so? Apart from the already-noted discrepancy of accessing the resource for a cached vs. live hit, generating a session would necessitate producing the session cookie. Returning that cookie would invalidate the resource as cacheable. Further, one of the significant advantages of a reverse-proxy caching strategy is to provide protection in a DoS attack, and the user agents in such an attack are very unlikely to maintain a session. Thus, if we were failing to produce a cache on initial hits to allow setting a session, &lt;strong&gt;all&lt;/strong&gt; those DoS hits would reach Interchange, and on top of that be churning out session entries on the server.&lt;/li&gt;
&lt;li&gt;Session writing is suppressed on any response with a cacheable resource. Interchange must treat the response without any permanence because all accesses of the resource from the cache will &lt;em&gt;never&lt;/em&gt; reach Interchange. If the request that produced the cache also wrote that user’s session, it would produce a deviation in behavior between the cached v. live requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;overrides-on-a-cacheable-response&#34;&gt;Overrides on a Cacheable Response&lt;/h3&gt;
&lt;p&gt;Any action resulting in a POST is considered to imply the necessity of the user initiating the request reaching the session (or the database, or some other permanent storage controlled by the Interchange app). Thus POSTs by default force the Volatile setting to true. However, note this can be overridden by the developer if necessary (e.g., if a DoS hits the home page with a POST rather than the expected GET).&lt;/p&gt;
&lt;p&gt;Similarly, any requests passing through either the “process” or “order” actionmap are assumed to require access to the session. “process” will most often be issued as a POST as well, although using “order” with a GET is common.&lt;/p&gt;
&lt;h3 id=&#34;user-state-on-cacheable-resources&#34;&gt;User State on Cacheable Resources&lt;/h3&gt;
&lt;p&gt;A big mistake a developer may make when considering full-page caching is to assume an all-or-nothing approach. Trying to compartmentalize an entire catalog into fully cacheable resources would be a daunting task, requiring essentially the construction of a fully client-side application and session management. This is neither realistic nor desirable.&lt;/p&gt;
&lt;p&gt;A catalog can gain considerable benefit simply from evaluating those resources which do not require session entanglement at all and starting with them. Without considering users that are logged in or have items in their cart, under most circumstances the home and category list pages should be free from entanglement. With a bit of URL management, the resources can skip the cache when a user is logged in or has items in the cart.&lt;/p&gt;
&lt;h4 id=&#34;read-only-user-state&#34;&gt;“Read Only” User State&lt;/h4&gt;
&lt;p&gt;If caching is desirable on resources that cannot be decoupled from session influence, we can expose the necessary parts of the session to the client in the form of cookies and can refactor our document to contain client-side code to manage the session use. Typical examples of this would be personalization for logged in users, or the display of a small cart on all pages. The session data stored in the cookie is controlled exclusively by Interchange and is read-only on the client. Each time the session is accessed and updated, the cookie is re-written to the client.&lt;/p&gt;
&lt;p&gt;Management of such a process is relatively easy with modern JavaScript frameworks such as jQuery. As a typical example, one might need to replace the following session-dependent code&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if session logged_in]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Hi, [value fname]!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;with client-side management:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;span&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;fname_display&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;span&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and in the ready() event elsewhere with our session cookie data stored in valuesData:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (valuesData.fname) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;#fname_display&amp;#39;&lt;/span&gt;).replaceWith(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Hi, &amp;#39;&lt;/span&gt; + valuesData.fname + &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The OutputCookieHook was developed as a convenient mechanism for constructing the proposed valuesData cookie above, allowing for a subroutine to build the cookie just prior to the core code that constructs the document headers but after any standard actions that would alter the session and would need to be captured in the cookie data.&lt;/p&gt;
&lt;h4 id=&#34;read-write-user-state&#34;&gt;“Read Write” User State&lt;/h4&gt;
&lt;p&gt;If state needs are more complex on a particularly popular resource, it may be necessary to allow our state cookie to also be updated from the client. With the tools described here, the developer can either amend the existing cookie, or construct a new one, that captures data input by the client through subsequent requests to cached resources. Once the user issues the next non-cached request, an Autoload subroutine could be constructed to identify that changes have occurred and then sync those changes back to the user’s session. While implementing read-write user state may be challenging, it is possible and has been done at End Point for clients where that need exists.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Hopefully this provides a good idea of how to get started when approaching full-page caching, which is possible in most web app frameworks, and soon will be much easier in Interchange 5 with the new core tools introduced here.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Interchange Search Caching with “Permanent More”</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/01/interchange-search-caching-with/"/>
      <id>https://www.endpointdev.com/blog/2012/01/interchange-search-caching-with/</id>
      <published>2012-01-02T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;Most sites that use Interchange take advantage of Interchange’s “more lists”. These are built-in tools that support an Interchange “search” (either the search/scan action, or result of direct SQL via [query]) to make it very easy to paginate results. Under the hood, the more list is a drill-in to a cached “search object”, so each page brings back a slice from the cache of the original search. There are extensive ways to modify the look and behavior of more lists and, with a bit of effort, they can be configured to meet design requirements.&lt;/p&gt;
&lt;p&gt;Where more lists tend to fall short, however, is with respect to SEO. There are two primary SEO deficiencies that get business stakeholders’ attention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is little control over the construction of the URLs for more lists. They leverage the scan actionmap and contain a hash key for the search object and numeric data to identify the slice and page location. They possess no intrinsic value in identifying the content they reference.&lt;/li&gt;
&lt;li&gt;The search cache by default is ephemeral and session-specific. This means all those results beyond page 1 the search engine has cataloged will result in dead links for search users who try to land directly on the more-listed pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is the latter issue that I wish to address because there is—​and has been for some time now—​a simple mechanism called “permanent more” to remedy the default behavior.&lt;/p&gt;
&lt;p&gt;You can leverage “permanent more” by adding the boolean &lt;strong&gt;mv_more_permanent&lt;/strong&gt;, or the shorthand &lt;strong&gt;pm&lt;/strong&gt;, to your search conditions. E.g.:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Link:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;[area search=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        co=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sf=category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        se=Foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        op=rm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        more=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ml=5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;b&amp;gt;pm=1&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;]&amp;#34;&amp;gt;All Foos&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Loop:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [loop search=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        co=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sf=category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        se=Foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        op=rm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        more=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ml=5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;b&amp;gt;pm=1&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...loop body with [more-list]...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [/loop]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Query:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        more=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ml=10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;b&amp;gt;pm=1&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sql=&amp;#34;SELECT * FROM products WHERE category LIKE &amp;#39;%Foo%&amp;#39;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...same as loop but with 10 matches/page...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [/query]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the initial search is defined with the “permanent more” setting, it will produce the following adjustments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The hash key used to store and identify the search cache is deterministic based on the search conditions. Many searches for Interchange are category driven. Thus, all end users who wish to browse a category end up clicking identical links, which create duplicate search caches, belonging uniquely to them. With permanent more, they all share the same cache, with the same identifier. As long as the search conditions don’t change, neither does the cache identifier. Even as the cache is refreshed with new executions of the search, the object remains in the same location. Thus, the results a search engine produced this morning reference links still valid now, tomorrow, or next week, provided they reference the same search conditions.&lt;/li&gt;
&lt;li&gt;The cached search object has no session affinity. Any link referencing the cache with the correct hash key has access to the content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Taken together, “permanent more” removes (for the most part, addressed later) dead links from more lists cataloged by search engines. There are, however, other benefits to “permanent more” beyond those intended as described above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As stated in passing, standard Interchange search caching produces duplicate search objects for common search conditions. For a busy site, these caches can have an impact on storage. Typically, maintenance is implemented to clean up cache files for all such files whose age exceeds by some amount the session duration (standard is 48 hours). With permanent more, duplicate caches are eliminated. A cache location is reused by all users with the same search requirements, keeping data-storage requirements for caches to the minimum necessary. As searches change, ophaned caches can still easily be cleaned up as they will immediately start to age with no more access to them necessary for storage.&lt;/li&gt;
&lt;li&gt;For the same reason that “permanent more” resolves search-engine links, it also resolves content management for individual sites using a reverse proxy for caching. Because most (and certainly the easiest) caching keys are based off of URL, the deterministic nature of the hash keys for “permanent more” allows assurance that the cached content in the proxy accurately reflects the search content over time, and that all users will hit the cached resource and not generate new, unique links with varying hash keys.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One shortcoming of “permanent more” to be aware of is the impact of changing data underneath the search. Even if search conditions do not change, the count and order of matching record sets may. So, e.g., enough products may be removed from a given category to cause the last page of a more list to become empty, which would cause any specific link into that page to become dead. More minor, but still a possibility, is the introduction or removal of products so that a particularly searched-for term has been “bumped” to another page within the search cache since the last time the search engine crawled the more lists. For searches backed by particularly volatile data, “permanent more” may not be sufficient to address search-engine or caching demands.&lt;/p&gt;
&lt;p&gt;Finally, “permanent more” should be avoided for any search features that may cache data sensitive to an individual user. This is unlikely to happen as, under most circumstances, the configuration of the search itself will change based on the unique characteristics of the user executing the search (e.g., a username included in a query to review order history). However, it is still possible that context-sensitive information could be stored in the search object and, if so, all other users with access to the more lists would have access to that information.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree Performance Benchmarking</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/05/spree-performance-benchmarking/"/>
      <id>https://www.endpointdev.com/blog/2011/05/spree-performance-benchmarking/</id>
      <published>2011-05-25T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;I see a lot of questions regarding Spree performance in the &lt;a href=&#34;https://groups.google.com/forum/#!forum/spree-user&#34;&gt;spree-user group&lt;/a&gt;, but they are rarely answered with metrics. I put together a quick script using the generic benchmark tool &lt;a href=&#34;https://httpd.apache.org/docs/2.0/programs/ab.html&#34;&gt;ab&lt;/a&gt; to review some data. Obviously, the answer to how well a site performs and scales is highly dependent on the host and the consumption of the web application, so the data here needs to be taken with a grain of salt. Another thing to note is that only two of the following use cases are running on Rails 3.0 — many of our current Spree clients are on Spree 0.11.2 or older. I also included one non-Spree Rails ecommerce application, in addition to a few non-Rails applications for comparison. All of the tests were run from my home network, so in theory there shouldn’t be bias on performance tests for sites running on End Point servers.&lt;/p&gt;
&lt;table cellpadding=&#34;5&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;&lt;tr&gt;   &lt;td&gt;&lt;/td&gt;   &lt;td align=&#34;center&#34; colspan=&#34;4&#34;&gt;ab -n 100&lt;/td&gt;   &lt;td&gt;&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 2 homepage&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 20 homepage&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 2&lt;br/&gt;
product page&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 20&lt;br/&gt;
product page&lt;/td&gt;   &lt;td&gt;&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #1&lt;/b&gt;&lt;br/&gt;
Spree: 0.11.2&lt;br/&gt;
Hosting: 4 cores, 512 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: &lt;100   &lt;/td&gt;   &lt;td&gt;7.49&lt;/td&gt;   &lt;td&gt;24.75&lt;/td&gt;   &lt;td&gt;6.49&lt;/td&gt;   &lt;td&gt;19.87&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;266.889&lt;/td&gt;&lt;td&gt;808.041&lt;/td&gt;&lt;td&gt;307.997&lt;/td&gt;&lt;td&gt;1006.552&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #2&lt;/b&gt;&lt;br/&gt;
Spree 0.11.2&lt;br/&gt;
Hosting: Engineyard, medium instance&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 100s   &lt;/td&gt;   &lt;td&gt;5.32&lt;/td&gt;&lt;td&gt;20.28&lt;/td&gt;&lt;td&gt;5.36&lt;/td&gt;&lt;td&gt;18.03&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;375.713&lt;/td&gt;&lt;td&gt;986.309&lt;/td&gt;&lt;td&gt;373.289&lt;/td&gt;&lt;td&gt;1109.524&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #3&lt;/b&gt;&lt;br/&gt;
Spree: 0.9.0&lt;br/&gt;
Hosting: 4 cores, 1 GB RAM&lt;br/&gt;
DB: PostgreSQL&lt;br/&gt;
# Products: &lt;100   &lt;/td&gt;   &lt;td&gt;4.91&lt;/td&gt;&lt;td&gt;25.39&lt;/td&gt;&lt;td&gt;1.98&lt;/td&gt;&lt;td&gt;6.54&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;407.135&lt;/td&gt;&lt;td&gt;787.782&lt;/td&gt;&lt;td&gt;1011.875&lt;/td&gt;&lt;td&gt;3060.062&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;(Former) Client #4&lt;/b&gt;&lt;br/&gt;
Spree: 0.11.2&lt;br/&gt;
Hosting: Unknown&lt;br/&gt;
DB: PostgreSQL&lt;br/&gt;
# Products: &gt;5000   &lt;/td&gt;   &lt;td&gt;20.69&lt;/td&gt;&lt;td&gt;8.84&lt;/td&gt;&lt;td&gt;10.15&lt;/td&gt;&lt;td&gt;19.28&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;96.673&lt;/td&gt;&lt;td&gt;2262.105&lt;/td&gt;&lt;td&gt;196.996&lt;/td&gt;&lt;td&gt;1037.146&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #5&lt;/b&gt;&lt;br/&gt;
Spree: 0.11.2&lt;br/&gt;
Hosting: EngineYard, small instance&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 1   &lt;/td&gt;   &lt;td&gt;12.28&lt;/td&gt;&lt;td&gt;16.23&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;162.909&lt;/td&gt;&lt;td&gt;1231.945&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #6&lt;/b&gt;&lt;br/&gt;
Spree: 0.40&lt;br/&gt;
Hosting: 4 cores, 1 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 50-100   &lt;/td&gt;   &lt;td&gt;3.61&lt;/td&gt;&lt;td&gt;8.93&lt;/td&gt;&lt;td&gt;2.96&lt;/td&gt;&lt;td&gt;3.06&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;553.569&lt;/td&gt;&lt;td&gt;2240.657&lt;/td&gt;&lt;td&gt;675.306&lt;/td&gt;&lt;td&gt;6539.433&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;SpreeDemo&lt;/b&gt;&lt;br/&gt;
Spree: Edge&lt;br/&gt;
Hosting: Heroku, 2 dynos&lt;br/&gt;
DB: Unknown&lt;br/&gt;
# Products: 100s   &lt;/td&gt;   &lt;td&gt;8.17&lt;/td&gt;&lt;td&gt;12.79&lt;/td&gt;&lt;td&gt;4.7&lt;/td&gt;&lt;td&gt;5.48&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;244.831&lt;/td&gt;&lt;td&gt;1563.642&lt;/td&gt;&lt;td&gt;425.27&lt;/td&gt;&lt;td&gt;3652.927&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #7&lt;/b&gt;&lt;br/&gt;
*custom Rails ecommerce app&lt;br/&gt;
Hosting: 1.0 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 1000s   &lt;/td&gt;   &lt;td&gt;5.43&lt;/td&gt;&lt;td&gt;29.8&lt;/td&gt;&lt;td&gt;4.45&lt;/td&gt;&lt;td&gt;23.14&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;368.409&lt;/td&gt;&lt;td&gt;671.082&lt;/td&gt;&lt;td&gt;448.962&lt;/td&gt;&lt;td&gt;864.24&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Interchange Demo&lt;/b&gt;&lt;br/&gt;
Hosting: 4 cores, 2 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: &gt;500   &lt;/td&gt;   &lt;td&gt;7.41&lt;/td&gt;&lt;td&gt;55.27&lt;/td&gt;&lt;td&gt;7.5&lt;/td&gt;&lt;td&gt;13.93&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;269.942&lt;/td&gt;&lt;td&gt;361.875&lt;/td&gt;&lt;td&gt;266.492&lt;/td&gt;&lt;td&gt;1435.51&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #8&lt;/b&gt;&lt;br/&gt;
*PHP site,&lt;br/&gt;
serves fully cached pages&lt;br/&gt;
with nginx with no app server or db hits&lt;br/&gt;
Hosting: 4 cores, 4 GB RAM   &lt;/td&gt;   &lt;td&gt;10.81&lt;/td&gt;&lt;td&gt;30.54&lt;/td&gt;&lt;td&gt;6.05&lt;/td&gt;&lt;td&gt;9.87&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;184.994&lt;/td&gt;&lt;td&gt;654.858&lt;/td&gt;&lt;td&gt;330.727&lt;/td&gt;&lt;td&gt;2027.092&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Magento Demo&lt;/b&gt;&lt;br/&gt;
Hosting: Unknown&lt;br/&gt;
DB: Unknown&lt;br/&gt;
# Products: 100s   &lt;/td&gt;   &lt;td&gt;4.26&lt;/td&gt;&lt;td&gt;44.85&lt;/td&gt;&lt;td&gt;2.68&lt;/td&gt;&lt;td&gt;36.29&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;469.831&lt;/td&gt;&lt;td&gt;445.931&lt;/td&gt;&lt;td&gt;745.472&lt;/td&gt;&lt;td&gt;551.11&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Here’s the same data in graphical form:&lt;/p&gt;
&lt;h3 id=&#34;requests-per-second&#34;&gt;Requests per Second&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/05/spree-performance-benchmarking/image-0-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5610751500251002546&#34; src=&#34;/blog/2011/05/spree-performance-benchmarking/image-0.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 740px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;time-per-request-ms&#34;&gt;Time Per Request (ms)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/05/spree-performance-benchmarking/image-1-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5610751511624706978&#34; src=&#34;/blog/2011/05/spree-performance-benchmarking/image-1.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 740px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We expect to see high performance on some of the sites with significant performance optimization. On smaller VPS, we expect to see the the server choke with higher concurrency.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Surge 2010 wrap-up</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/10/surge-2010-wrap-up/"/>
      <id>https://www.endpointdev.com/blog/2010/10/surge-2010-wrap-up/</id>
      <published>2010-10-05T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Following up on my earlier post about &lt;a href=&#34;/blog/2010/10/surge-2010-day-1/&#34;&gt;day 1 of the conference&lt;/a&gt;, here is an unsorted collection of what I felt were noteworthy observations made in talks at &lt;a href=&#34;https://web.archive.org/web/20101013213822/http://omniti.com/surge/2010&#34;&gt;Surge 2010&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Web engineering as a separate discipline from computer science or software development started around 1999. It is interdisciplinary, involving &lt;a href=&#34;https://en.wikipedia.org/wiki/Human_factors_and_ergonomics&#34;&gt;human factors engineering&lt;/a&gt;, &lt;a href=&#34;https://en.wikipedia.org/wiki/Systems_engineering&#34;&gt;systems engineering&lt;/a&gt;, &lt;a href=&#34;https://en.wikipedia.org/wiki/Operations_research&#34;&gt;operations research&lt;/a&gt;, &lt;a href=&#34;https://en.wikipedia.org/wiki/Fault_tolerance&#34;&gt;fault-tolerant design&lt;/a&gt;, and &lt;a href=&#34;https://en.wikipedia.org/wiki/Control_engineering&#34;&gt;control systems engineering&lt;/a&gt;. (John Allspaw)&lt;/p&gt;
&lt;p&gt;A real-time system is one in which the correctness of a system is tied to its timeliness. Eventual consistency is an oxymoron if timeliness is part of the data itself. Caching by CDNs can’t solve our problems here. (Bryan Cantrill)&lt;/p&gt;
&lt;p&gt;Pre-fab metrics are worth less (and maybe worthless) when not tied to something in your business. Message queues enable lots of new uses because of the ability to have multiple observers. See &lt;a href=&#34;http://www.espertech.com/&#34;&gt;Esper&lt;/a&gt; (Java, GPL) for live ongoing SQL-like queries of messages from AMQP sources, etc. (Theo Schlossnagle)&lt;/p&gt;
&lt;p&gt;On scaling up vs. out: If your numbers show “up” is enough, be happy you can keep your system simpler. (Theo Schlossnagle)&lt;/p&gt;
&lt;p&gt;Anyone can only ever know the past in a distributed system. There’s no such thing as global state in reality. Our systems are always at least slightly inconsistent with the world. “Eventually consistent” just acknowledges the reality of delay and focuses on measuring and dealing with that. (Justin Sheehy)&lt;/p&gt;
&lt;p&gt;Reliability compared to resiliency: Being resilient means success of your mission despite partial failure of components. How do you deal with failure? Degrade, and know before your users do. (Justin Sheehy)&lt;/p&gt;
&lt;p&gt;Build in monitoring during development, so it’s not a bottleneck right before deployment. (John Allspaw)&lt;/p&gt;
&lt;p&gt;Data comes from the devil. Models come from God. Data + Models = Insight. Data needs to be put in a prison (a model) and made to confess the truth. Measurement is a process. Numbers aren’t right. What is the error range? Visualization is helpful, but analyze the raw data looking for anomalies (such as &amp;gt; 100% efficiency, etc.). VAMOOS = visualize, analyze, modelize, over &amp;amp; over till satisfied. (Neil Gunther)&lt;/p&gt;
&lt;p&gt;Anycast for DNS alone tends to localize on the user’s recursive resolver, not their actual location. Anycast for the actual content delivery automatically localizes on the user’s actual location. (Tom Daly)&lt;/p&gt;
&lt;p&gt;To scale up, add more capacity to do X, make system do X faster, or stop doing so much X. What makes a task take time? It’s utilizing a resource, it’s waiting for a response, or it’s waiting for synchronization. “Shard early &amp;amp; often” is expensive &amp;amp; unnecessary for most situations. Sharding makes sense when write demand exceeds capacity. (Baron Schwartz)&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing&#34;&gt;Eight fallacies of distributed computing&lt;/a&gt; were discussed by Ruslan Belkin.&lt;/p&gt;
&lt;p&gt;Mike Mallone of SimpleGeo gave a fascinating talk on &lt;a href=&#34;https://web.archive.org/web/20101017013227/http://omniti.com/surge/2010/speakers/mike-malone&#34;&gt;working with geolocation data in Cassandra&lt;/a&gt;. It’d be wonderful to see an open source release of their order-preserving partitioner that allows for range queries in a single dimension. Or to start with, just the slides from Mike’s talk!&lt;/p&gt;
&lt;p&gt;In summary, it was a very good conference!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Surge 2010 day 1</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/10/surge-2010-day-1/"/>
      <id>https://www.endpointdev.com/blog/2010/10/surge-2010-day-1/</id>
      <published>2010-10-01T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Today (technically, yesterday) was the first day of the &lt;a href=&#34;https://web.archive.org/web/20101015004118/http://omniti.com/surge/2010/&#34;&gt;Surge 2010&lt;/a&gt; conference in Baltimore, Maryland. The Tremont Grand venue is perfect for a conference. The old Masonic lodge makes for great meeting rooms, and having a hallway connect it to the hotel was nice to avoid the heavy rain today. The conference organization and scheduling and Internet have all been solid. Well done!&lt;/p&gt;
&lt;p&gt;There were a lot of great talks, but I wanted to focus on just one that was very interesting to me: &lt;a href=&#34;https://web.archive.org/web/20101127040749/http://omniti.com/surge/2010/speakers/artur-bergman&#34;&gt;Artur Bergman&lt;/a&gt;’s on scaling Wikia. Some points of interest:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;They (ab)use Google Analytics to track other things besides the typical pages viewed by whom, when. For example, page load time as measured by JavaScript, with data sent to a separate GA profile for analysis separately from normal traffic. That is then correlated with exit rates to give an idea of the benefit of page delivery speed in terms of user stickiness.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;They use the excellent Varnish reverse proxy cache.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;500 errors from the origin result in a static page served by Varnish, with error data hitting a separate Google Analytics profile.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;They have both geographically distributed servers and team.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;They’ve found SSDs (solid state disks) to be well worth the extra cost: fast, using less power in a given server, and requiring fewer servers overall. They have to use Linux software RAID because no hardware RAID controllers they’ve tested could keep up with the speed of SSD. They have run into the known problems with disk write performance dropping as they fill and recycle, but haven’t found it to be a problem when used on replaceable cache machines.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;They run their own CDN, with nodes running Varnish, Quagga (for BGP routing), BIND, and Scribe. But they use Akamai for static content.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Even running Varnish with 1 second TTL can save your backend app servers when heavy traffic arrives! One hit per second is no problem; thousands may mean meltdown.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Serving stale cached content when the backend is down can be a good choice. It means most visitors will never know anything was wrong. (Depends on the site’s functions, of course.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Their backup datacenter in Iowa is in a former nuclear bunker. See &lt;a href=&#34;https://web.archive.org/web/20101030001235/http://ganglia.wikia.net/iowa/&#34;&gt;monitoring graphs&lt;/a&gt; for it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wikia ops staff interact with their users via IRC. This “crowdsourced monitoring” has resulted in a competition between Wikia ops people and the users to see who can spot outages first.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Having their own hardware in multiple redundant datacenters has meant much more leverage in pricing discussions with datacenters. “We can just move.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;They own their own hardware, and run on bare metal. At no time does user traffic pass through any virtualized systems at all. The performance just isn’t there. They do use virtual machines for some external monitoring stuff.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;They use Riak for N-master inter-datacenter synchronization, and RiakFS for sessions and files. RiakFS is for the “legacy” MediaWiki need for POSIX access to files, but they can serve those files to the general public from Riak’s HTTP interface via Varnish cache.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;They use VPN tunnels between datacenters. Sometimes using their own routes, even over multiple hops, leads to faster transit than going over the public Internet.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lots of interesting custom VCL (Varnish Configuration Language) examples.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This had plenty of interesting things to consider for any web application architecture.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Distributed Transactions and Two-Phase Commit</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/07/distributed-transactions-and-two-phase/"/>
      <id>https://www.endpointdev.com/blog/2010/07/distributed-transactions-and-two-phase/</id>
      <published>2010-07-29T00:00:00+00:00</published>
      <author>
        <name>Josh Tolley</name>
      </author>
      <content type="html">
        &lt;p&gt;The typical example of a transaction involves Alice and Bob, and their bank. Alice pays Bob $100, and the bank needs to debit Alice and credit Bob. Easy enough, provided the server doesn’t crash. But what happens if the bank debits Alice, and then before crediting Bob, the server goes down? Or what if they credit Bob first, and then try to debit Alice only to find she doesn’t have enough funds? A transaction allows the debit and credit operations to happen as a package (“atomically” is the word commonly used), so either both operations happen or neither happens, even if the server crashes halfway through the transaction. That way the bank never credits Bob without debiting Alice, or vice versa.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/distributed-transactions-and-two-phase/image-0-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5499431340481218818&#34; src=&#34;/blog/2010/07/distributed-transactions-and-two-phase/image-0.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 220px; height: 124px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That’s simple enough, but the situation can become more complex. What if, for instance, for buzzword-compliance purposes, the bank has “sharded” its accounts database by splitting it in pieces and putting each piece on a different server (whether this is would be smart or not is outside the scope of this post). The typical transaction handles statements issued only for one database, so we can’t wrap the debit and credit operations within a single BEGIN/COMMIT if Alice’s account information lives on one server and Bob’s lives on another.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/distributed-transactions-and-two-phase/image-1-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5499430402449915634&#34; src=&#34;/blog/2010/07/distributed-transactions-and-two-phase/image-1.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 255px; height: 184px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Enter “distributed transactions”. A distributed transaction allows applications to group multiple transaction-aware systems into a single transaction. These systems might be different databases, or they might include other systems such as message queues, in which case the transaction concept means a message would get delivered if and only if the rest of the transaction completed. So with a distributed transaction, the bank could debit Alice’s account in one database and credit Bob’s in another, atomically.&lt;/p&gt;
&lt;p&gt;All this comes at some cost. Distributed transactions require a “transaction manager”, an application which handles the special semantics required to commit a distributed transaction. Second, the systems involved must support “two-phase commit” (which was added to PostgreSQL in version 8.1). Distributed transactions are committed using PREPARE TRANSACTION &amp;lsquo;foo&amp;rsquo; (phase 1), and COMMIT PREPARED &amp;lsquo;foo&amp;rsquo; or ROLLBACK PREPARED &amp;lsquo;foo&amp;rsquo; (phase 2), rather than the usual COMMIT or ROLLBACK.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/distributed-transactions-and-two-phase/image-2-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5499430097935795154&#34; src=&#34;/blog/2010/07/distributed-transactions-and-two-phase/image-2.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 231px; height: 237px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The beginning of a distributed transaction looks just like any other transaction: the application issues a BEGIN statement (optional in PostgreSQL), followed by normal SQL statements. When the transaction manager is instructed to commit, it runs the first commit phase by saying “PREPARE TRANSACTION &amp;lsquo;foo&amp;rsquo;” (where “foo” is some arbitrary identifier for this transaction) on each system involved in the distributed transaction. Each system does whatever it needs to do to determine whether or not this transaction can be committed and to make sure it can be committed even if the server crashes, and reports success or failure. If all systems succeed, the transaction manager follows up with “COMMIT PREPARED &amp;lsquo;foo&amp;rsquo;”, and if a system reports failure, the transaction manager can roll back all the other systems using either ROLLBACK (for those transactions it hasn’t yet prepared), or “ROLLBACK PREPARED &amp;lsquo;foo&amp;rsquo;”. Using two-phase commit is obviously slower than committing transactions on only one database, but sometimes the data integrity it provides justifies the extra cost.&lt;/p&gt;
&lt;p&gt;In PostgreSQL, two-phase commit is supported provided max_prepared_transactions is nonzero. A PREPARE TRANSACTION statement persists the current transaction to disk, and dissociates it from the current session. That way it can survive even if the database goes down. The current session no longer has an active transaction. However, the prepared transaction acts like any other open transaction in that all locks held by the prepared transaction remain held, and VACUUM cannot reclaim storage from that transaction. So it’s not a good idea to leave prepared transactions open for a long time.&lt;/p&gt;
&lt;p&gt;Distributed transactions are most common, it seems, in Java applications. Full J2EE application servers typically come with a transaction manager component. For my examples I’ll use an open source, standalone transaction manager, called &lt;a href=&#34;https://github.com/bitronix/btm&#34;&gt;Bitronix&lt;/a&gt;. I’m not particularly fond of using Java for simple scripts, though, so I’ve used JRuby for this &lt;a href=&#34;https://josh.endpointdev.com/bitronix.rb&#34;&gt;demonstration code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This script uses two databases, which I’ve called “athos” and “porthos”. Each has &lt;a href=&#34;https://josh.endpointdev.com/athos.sql&#34;&gt;same schema&lt;/a&gt;, which provides a simple framework for the sharded bank example described above. This schema provides a table for account names, another for ledger information, and a simple trigger to raise an exception when a transaction would bring a person’s balance below $0. I’ll first populate athos with Alice’s account information. She gets $200 to start. Bob will go in the porthos database, with no initial balance.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5432 josh@athos# insert into accounts values (&amp;#39;Alice&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSERT 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5432 josh@athos*# insert into ledger values (&amp;#39;Alice&amp;#39;, 200);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSERT 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5432 josh@athos*# commit;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;COMMIT5432 josh@athos# \c porthos
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;You are now connected to database &amp;#34;porthos&amp;#34;.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5432 josh@porthos# insert into accounts values (&amp;#39;Bob&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSERT 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5432 josh@porthos*# commit;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;COMMIT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Use of Bitronix is pretty straightforward. After setting up a few constants for easier typing, I create a Bitronix data source for each PostgreSQL database. Here I have to use the PostgreSQL JDBC driver’s org.postgresql.xa.PGXADataSource class; “XA” is Java’s protocol for two-phase commit, and requires JDBC driver support. Here’s the code for setting up one data source; the other is just the same.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ds1 = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;PDS&lt;/span&gt;.new
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ds1.set_class_name &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;org.postgresql.xa.PGXADataSource&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ds1.set_unique_name &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;pgsql1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ds1.set_max_pool_size &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ds1.get_driver_properties.set_property &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;databaseName&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;athos&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ds1.get_driver_properties.set_property &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;user&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;josh&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ds1.init&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then I simply get a connection from each data source, instantiate a Bitronix TransactionManager object, and begin a transaction.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c1 = ds1.get_connection
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c2 = ds2.get_connection
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;btm = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;TxnSvc&lt;/span&gt;.get_transaction_manager
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;btm.begin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Within my transaction, I just use normal JDBC commands to debit Alice and credit Bob, after which I commit the transaction through the TransactionManager object. If this transaction fails, it raises an exception, which I can capture using Ruby’s begin/rescue exception handling, and roll back the transaction.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  s2 = c2.prepare_statement &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;INSERT INTO ledger VALUES (&amp;#39;Bob&amp;#39;, 100)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  s2.execute_update
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  s2.close
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  s1 = c1.prepare_statement &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;INSERT INTO ledger VALUES (&amp;#39;Alice&amp;#39;, -100)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  s1.execute_update
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  s1.close
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  btm.commit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Successfully committed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;rescue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Something bad happened: &amp;#34;&lt;/span&gt; + &lt;span style=&#34;color:#d70&#34;&gt;$!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  btm.rollback
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When I run this, Bitronix gives me a bunch of output, which I haven’t bothered to suppress, but among it all is the “Successfully committed” string I told it to print on success. Since Alice is debited $100 each time we run this, and she started with $200, we can run it twice before hitting errors. On the third time, we get this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Something bad happened: org.postgresql.util.PSQLException: ERROR: Rejecting operation; account owner Alice’s balance would drop below 0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is our trigger firing, to tell us that we can’t debit Alice any more. If I look in the two databases, I can see that everything worked as planned:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5432 josh@athos*# select get_balance(&amp;#39;Alice&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; get_balance 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(1 row)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5432 josh@athos*# \c porthos 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;You are now connected to database &amp;#34;porthos&amp;#34;.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5432 josh@porthos# select get_balance(&amp;#39;Bob&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; get_balance 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         200
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(1 row)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Remember I’ve run my script three times, but Bob has only been credited $200, because that’s all Alice had to start with.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>PostgreSQL: Migration Support Checklist</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/07/postgresql-migration-support-checklist/"/>
      <id>https://www.endpointdev.com/blog/2010/07/postgresql-migration-support-checklist/</id>
      <published>2010-07-22T00:00:00+00:00</published>
      <author>
        <name>David Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;A database migration (be it from some other database to PostgreSQL, or even from an older version of PostgreSQL to a nice shiny new one) can be a complicated procedure with many details and many moving parts. I’ve found it helpful to construct a list of questions in order to make sure that you’re considering all aspects of the migrations and gauge the scope of what will be involved.  This list includes questions we ask our clients; feel free to contribute your own additional considerations or suggestions.&lt;/p&gt;
&lt;p&gt;Technical questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Database servers:&lt;/strong&gt; How many database servers do you have? For each, what are the basic system specifications (OS, CPU architecture, 32- vs 64-bit, RAM, disk, etc)? What kind of storage are you using for the existing database, and what do you plan to use for the new database? Direct-attached storage (SAS, SATA, etc.), SAN (what vendor?), or other? Do you use any configuration management system such as Puppet, Chef, etc.?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Application servers and other remote access:&lt;/strong&gt; How many application servers do you have? For each, what are the basic system specifications (OS, CPU architecture, 32- vs 64-bit, RAM, disk, etc)?  Do you use any configuration management system such as Puppet, Chef, etc.? What other network considerations are there? Is ODBC used, or SSL transport, any VPNs? Are multiple datacenters involved? How about egress/ingress firewalls?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Middleware:&lt;/strong&gt; Do you currently use any sort of connection pooling, load balancing, or other middleware between your application and database servers?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data needs:&lt;/strong&gt; Can you describe your data access patterns? i.e., is the majority of your data historical and rarely accessed? Are there any existing reporting needs that will need to be duplicated on the PostgreSQL system? Do you already have reports of database usage, including traffic levels, frequent or intensive queries, etc?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Size:&lt;/strong&gt; What kind of transaction volume do you see? How large are your databases? How many tables do you have and what is the size of the larger ones? How many users or database connections will you need to support?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Backups:&lt;/strong&gt; What are your current backup policies/procedures? How will these need to change with the move to PostgreSQL?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Replication/load balancing:&lt;/strong&gt; What kind of system redundancy do you currently have/need? Do you have any kind of database load-balancing or master-slave replication?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Monitoring:&lt;/strong&gt; What is the current monitoring/in-house support infrastructure? What needs to be duplicated, and can any portion of this facility be reused?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Interfaces:&lt;/strong&gt; What language are your applications written in, and what drivers exist to connect to your current database? Will there be a compatible driver available in your language of choice in order?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extensions:&lt;/strong&gt; Are you currently using any in-database procedures or functionality (i.e., in PL/SQL or another embedded language of choice)? If so, how many? What will the difficulty be in porting these functions to PostgreSQL?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And a couple of business-related questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scheduling:&lt;/strong&gt; What is the timeframe for transition? When can appropriate downtime be scheduled? How much database downtime can you afford?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Staffing:&lt;/strong&gt; Do you currently have in-house DBAs to manage the servers, etc on a day-to-day basis? Is there anyone with PostgreSQL experience or familiarity on staff?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Being able to answer all of these questions is critical to formulating a migration plan and carrying out a migration successfully.&lt;/p&gt;
&lt;p&gt;Particularly with the impending (July 2010) end of life for previous PostgreSQL releases 7.4, 8.0 and (in November 2010) 8.1, a database migration may be on your radar. End Point is one of many professional PostgreSQL support companies who would be happy to assist you in your transition.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Cassandra, Thrift, and Fibers in EventMachine</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/05/cassandra-thrift-and-fibers-in/"/>
      <id>https://www.endpointdev.com/blog/2010/05/cassandra-thrift-and-fibers-in/</id>
      <published>2010-05-08T00:00:00+00:00</published>
      <author>
        <name>Ethan Rowe</name>
      </author>
      <content type="html">
        &lt;p&gt;I’ve been working with Cassandra and EventMachine lately, in an attempt to maximize write throughput for bulk loading situations (and I would prefer to not abandon the pretty Ruby classes I have fronting Cassandra, hence EventMachine rather than hopping over to Java or Scala).&lt;/p&gt;
&lt;p&gt;The Thrift client transport for EventMachine requires the use of fibers. The documentation available for how fibers and EventMachine interact is not all that clear just yet, so perhaps documenting my adventures will be of use to somebody else.&lt;/p&gt;
&lt;h3 id=&#34;a-single-fiber-is-traditionally-imperative&#34;&gt;A single fiber is traditionally imperative&lt;/h3&gt;
&lt;p&gt;EventMachine puts the I/O on background threads, but your use of the I/O interface will interact with it as if it’s a traditional blocking operation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env ruby&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;eventmachine&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;thrift_client&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;thrift_client/event_machine&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;cassandra&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;get_client&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Cassandra&lt;/span&gt;.new(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Keyspace1&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;127.0.0.1:9160&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:transport_wrapper&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;nil&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:transport&lt;/span&gt;         =&amp;gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Thrift&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;EventMachineTransport&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;write&lt;/span&gt;(client, key, &lt;span style=&#34;color:#038&#34;&gt;hash&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Writing &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;key&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  client.insert(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Standard1&amp;#39;&lt;/span&gt;, key, &lt;span style=&#34;color:#038&#34;&gt;hash&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;puts&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Wrote &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;#{&lt;/span&gt;key&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;EM&lt;/span&gt;.run &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Fiber&lt;/span&gt;.new &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    client = get_client
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    write(client, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;, {&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;aard&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;vark&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    write(client, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;, {&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;platy&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;pus&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;EM&lt;/span&gt;.stop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;.resume
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The Thrift::EventMachine transport performs the actual Thrift network operations (connecting, sending data, receiving data) on a fiber in one of EventMachine’s background threads. But it manages the callbacks and errbacks internally so the client behaves in usual blocking manner and does not expose the asyncronous delights going on behind the scenes.&lt;/p&gt;
&lt;p&gt;Therefore, in the code snippet above, the “foo” row will be inserted first, and then the “bar” row. Every time. The output always is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Wrote foo.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Wrote bar.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above snippet is contrived, but it makes an import point: given two or more Thrift operations (like Cassandra inserts) that are logically independent of each other such that their order does not matter, you’re not necessarily gaining a lot if those operations happen in the same fiber.&lt;/p&gt;
&lt;h3 id=&#34;for-concurrency-use-multiple-fibers&#34;&gt;For concurrency, use multiple fibers&lt;/h3&gt;
&lt;p&gt;Now let’s replace the above code sample’s EM.run block with this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;EM&lt;/span&gt;.run &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#33b&#34;&gt;@done&lt;/span&gt; = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Fiber&lt;/span&gt;.new &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    write(get_client, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;, {&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;aard&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;vark&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@done&lt;/span&gt; += &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;.resume
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Fiber&lt;/span&gt;.new &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    write(get_client, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;, {&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;platy&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;pus&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#33b&#34;&gt;@done&lt;/span&gt; += &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;                 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;.resume                   
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;EM&lt;/span&gt;.add_periodic_timer(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;) { &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;EM&lt;/span&gt;.stop &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#33b&#34;&gt;@done&lt;/span&gt; == &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt; } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You don’t know how this is going to play out, but the typical output proves the concurrent operation of the two fibers involved:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Writing foo.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Writing bar.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Wrote foo.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Wrote bar.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we were writing a larger number of rows out to Cassandra, we could expect to see a greater variety of interleaving between the respective fibers.&lt;/p&gt;
&lt;p&gt;Note a critical difference between the two examples. In the single-fiber example, we issue the EM.stop as the final step of the fiber. Because the single fiber proceeds serially, this makes sense. In the multi-fiber example, things run asyncronously, so we have no way of knowing for sure which fiber will complete first. Consequently, it’s necessary have some means of signifying that work is done and the EM can stop; in this lame example, the @done instance variable acts as this flag. In a more rigorous example, you might use a queue and a queue’s size to organize such things.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>PostgreSQL EC2/EBS/RAID 0 snapshot backup</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/02/postgresql-ec2-ebs-raid0-snapshot/"/>
      <id>https://www.endpointdev.com/blog/2010/02/postgresql-ec2-ebs-raid0-snapshot/</id>
      <published>2010-02-23T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;One of our clients uses &lt;a href=&#34;https://aws.amazon.com/&#34;&gt;Amazon Web Services&lt;/a&gt; to host their production application and database servers on EC2 with EBS (Elastic Block Store) storage volumes. Their main database is &lt;a href=&#34;/expertise/postgresql/&#34;&gt;PostgreSQL&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A big benefit of Amazon’s cloud services is that you can easily add and remove virtual server instances, storage space, etc. and pay as you go. One known problem with Amazon’s EBS storage is that it is much more I/O limited than, say, a nice SAN.&lt;/p&gt;
&lt;p&gt;To partially mitigate the I/O limitations, they’re using 4 EBS volumes to back a Linux software RAID 0 block device. On top of that is the xfs filesystem. This gives roughly 4x the I/O throughput and has been effective so far.&lt;/p&gt;
&lt;p&gt;They ship WAL files to a secondary server that serves as warm standby in case the primary server fails. That’s working fine.&lt;/p&gt;
&lt;p&gt;They also do nightly backups using pg_dumpall on the master so that there’s a separate portable (SQL) backup not dependent on the server architecture. The problem that led to this article is that extra I/O caused by pg_dumpall pushes the system beyond its I/O limits. It adds both reads (from the PostgreSQL database) and writes (to the SQL output file).&lt;/p&gt;
&lt;p&gt;There are several solutions we are considering so that we can keep both binary backups of the database and SQL backups, since both types are valuable. In this article I’m not discussing all the options or trying to decide which is best in this case. Instead, I want to consider just one of the tried and true methods of backing up the binary database files on another host to offload the I/O:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an atomic snapshot of the block devices&lt;/li&gt;
&lt;li&gt;Spin up another virtual server&lt;/li&gt;
&lt;li&gt;Mount the backup volume&lt;/li&gt;
&lt;li&gt;Start Postgres and allow it to recover from the apparent “crash” the server had (since there wasn’t a clean shutdown of the database before the snapshot&lt;/li&gt;
&lt;li&gt;Do whatever pg_dump or other backups are desired&lt;/li&gt;
&lt;li&gt;Make throwaway copies of the snapshot for QA or other testing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The benefit of such snapshots is that you get an exact backup of the database, with whatever table bloat, indexes, statistics, etc. exactly as they are in production. That’s a big difference from a freshly created database and import from pg_dump.&lt;/p&gt;
&lt;p&gt;The difference here is that we’re using 4 EBS volumes with RAID 0 striped across them, and there isn’t currently a way to do an atomic snapshot of all 4 volumes at the same time. So it’s no longer “atomic” and who knows what state the filesystem metadata and the file data itself would be in?&lt;/p&gt;
&lt;p&gt;Well, why not try it anyway? Filesystem metadata doesn’t change that often, especially in the controlled environment of a Postgres data volume. Snapshotting within a relatively short timeframe would be pretty close to atomic, and probably look to the software (operating system and database) like some kind of strange crash since some EBS volumes would have slightly newer writes than others. But aren’t all crashes a little unpredictable? Why shouldn’t the software be able to deal with that? Especially if we have Postgres make a checkpoint right before we snapshot.&lt;/p&gt;
&lt;p&gt;I wanted to know if it was crazy or not, so I tried it on a new set of services in a separate AWS account. Here are the notes and some details of what I did:&lt;/p&gt;
&lt;p&gt;1. Created one EC2 image:&lt;/p&gt;
&lt;p&gt;Amazon EC2 Debian 5.0 lenny AMI built by Eric Hammond&lt;br&gt;
Debian AMI ID ami-4ffe1926 (x86_64)&lt;br&gt;
Instance Type: High-CPU Extra Large (c1.xlarge) — 7 GB RAM, 8 CPU cores&lt;/p&gt;
&lt;p&gt;2. Created 4 x 10 GB EBS volumes&lt;/p&gt;
&lt;p&gt;3. Attached volumes to the image&lt;/p&gt;
&lt;p&gt;4. Created software RAID 0 device:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mdadm -C /dev/md0 -n &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt; -l &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; -z max /dev/sdf /dev/sdg /dev/sdh /dev/sdi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;5. Created XFS filesystem on top of RAID 0 device:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkfs -t xfs -L /pgdata /dev/md0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;6. Set up in /etc/fstab and mounted:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir /pgdata
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# edit /etc/fstab, with noatime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mount /pgdata&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;7. Installed PostgreSQL 8.3&lt;/p&gt;
&lt;p&gt;8. Configured postgresql.conf to be similar to primary production database server&lt;/p&gt;
&lt;p&gt;9. Created empty new database cluster with data directory in /pgdata&lt;/p&gt;
&lt;p&gt;10. Started Postgres and imported a play database (from public domain census name data and Project Gutenberg texts), resulting in about 820 MB in data directory&lt;/p&gt;
&lt;p&gt;11. Ran some bulk inserts to grow database to around 5 GB&lt;/p&gt;
&lt;p&gt;12. Rebooted EC2 instance to confirm everything came back up correctly on its own&lt;/p&gt;
&lt;p&gt;13. Set up two concurrent data-insertion processes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;50 million row insert based on another local table (INSERT INTO &amp;hellip; SELECT &amp;hellip;), in a single transaction (hits disk hard, but nothing should be visible in the snapshot because the transaction won’t have committed before the snapshot is taken)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Repeated single inserts in autocommit mode (Python script writing INSERT statements using random data from /usr/share/dict/words piped into psql), to verify that new inserts made it into the snapshot, and no partial row garbage leaked through&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;14. Started those “beater” jobs, which mostly consumed 2-3 CPU cores&lt;/p&gt;
&lt;p&gt;15. Manually inserted a known test row and created a known view that should appear in the snapshot&lt;/p&gt;
&lt;p&gt;16. Started Postgres’s backup mode that allows for copying binary data files in a non-atomic manner, which also does a CHECKPOINT and thus also a filesystem sync:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;pg_start_backup(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;raid_backup&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;17. Manually inserted a 2nd known test row &amp;amp; 2nd known test view that I don’t want to appear in the snapshot after recovery&lt;/p&gt;
&lt;p&gt;18. Ran snapshot script which calls ec2-create-snapshot on each of the 4 EBS volumes—​during first run, run serially quite slowly taking about 1 minute total; during second run, run in parallel such that the snapshot point was within 1 second for all 4 volumes&lt;/p&gt;
&lt;p&gt;19. Tell Postgres the backup’s over:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;pg_stop_backup();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;20. Ran script to create new EBS volumes derived from the 4 snapshots (which aren’t directly usable and always go into S3), using &lt;code&gt;ec2-create-volume --snapshot&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;21. Run script to attach new EBS volumes to devices on the new EC2 instance using &lt;code&gt;ec2-attach-volume&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;22. Then, on the new EC2 instance for doing backups:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mdadm --assemble --scan&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mount /pgdata&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Start Postgres&lt;/li&gt;
&lt;li&gt;Count rows on the 2 volatile tables; confirm that the table with the in-process transaction doesn’t show any new rows, and that the table getting individual rows committed to reads correctly&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VACUUM VERBOSE&lt;/code&gt; — and confirm no errors or inconsistencies detected&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pg_dumpall&lt;/code&gt; # confirmed no errors and data looks sound&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It worked! No errors or problems, and pretty straightforward to do.&lt;/p&gt;
&lt;p&gt;Actually before doing all the above I first did a simpler trial run with no active database writes happening, and didn’t make any attempt for the 4 EBS snapshots to happen simultaneously. They were actually spread out over almost a minute, and it worked fine. With the confidence that the whole thing wasn’t a fool’s errand, I then put together the scripts to do lots of writes during the snapshot and made the snapshots run in parallel so they’d be close to atomic.&lt;/p&gt;
&lt;p&gt;There are lots of caveats to note here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is an experiment in progress, not a how-to for the general public.&lt;/li&gt;
&lt;li&gt;The data set that was snapshotted was fairly small.&lt;/li&gt;
&lt;li&gt;Two successful runs, even with no failures, is not a very big sample set. :)&lt;/li&gt;
&lt;li&gt;I didn’t use Postgres’s point-in-time recovery (PITR) here at all—​I just started up the database and let Postgres recover from an apparent crash. Shipping over the few WAL logs from the master collected during the pg_backup run &lt;em&gt;after&lt;/em&gt; the snapshot copying is complete would allow a theoretically fully reliable recovery to be made, not just a practically non-failing recovery as I did above.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So there’s more work to be done to prove this technique viable in production for a mission-critical database, but it’s a promising start worth further investigation. It shows that there &lt;em&gt;is&lt;/em&gt; a way to back up a database across multiple EBS volumes without adding noticeably to its I/O load by utilizing the Amazon EBS data store’s snapshotting and letting a separate EC2 server offload the I/O of backups or anything else we want to do with the data.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Slony: Cascading Subscriptions</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/01/postgres-slony-cascading-subscription/"/>
      <id>https://www.endpointdev.com/blog/2010/01/postgres-slony-cascading-subscription/</id>
      <published>2010-01-28T00:00:00+00:00</published>
      <author>
        <name>David Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Sometime you run into a situation where you need to replicate one
dataset to many machines in multiple datacenters, with different costs
associated with sending to each (either real costs as in bandwidth, or
virtual costs as in the amount of time it takes to transmit to each
machine). Defining a Slony cluster to handle this is easy, as you can
specify the topology and paths taken to replicate any changes.&lt;/p&gt;
&lt;p&gt;Basic topology:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data center A, with machines A1, A2, A3, and A4.&lt;/li&gt;
&lt;li&gt;Data center B, with machines B1, B2, B3, and B4.&lt;/li&gt;
&lt;li&gt;Data center C, with machines C1, C2, C3, and C4.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://4.bp.blogspot.com/_eLhk5Eevkf8/S2H5apImCRI/AAAAAAAAABk/24-aTF5wp50/s1600-h/slony_non_cascaded_pathways.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5431896861699344658&#34; src=&#34;/blog/2010/01/postgres-slony-cascading-subscription/image-0.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 106px;&#34;/&gt;&lt;br/&gt;Figure 1: Non-cascaded slony replication nodes/pathways.
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Node A1 is the master, which propagates its changes to all other
machines. In the simple setup, A1 would push all of its changes to
each node, however if data centers B and C have high costs associated
with transfer to the nodes, you end up transferring 4x the data needed
for each data center. (We are assuming that traffic on the local
subnet at each data center is cheap and fast.)&lt;/p&gt;
&lt;p&gt;The basic idea then, is to push the changes only once to each
datacenter, and let the “master” machine in the data center push the
changes out to the others in the data center. This reduces traffic
from the master to each datacenter, plus removes any other associated
costs associated with pushing to every node.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://2.bp.blogspot.com/_eLhk5Eevkf8/S2H56IeyG1I/AAAAAAAAABs/_LxqX_P0n5I/s1600-h/slony_cascaded_pathways.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5431897402689854290&#34; src=&#34;/blog/2010/01/postgres-slony-cascading-subscription/image-1.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 166px;&#34;/&gt;&lt;br/&gt;
Figure 2: Cascaded slony replication nodes/pathways&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let’s look at an example configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cluster_init.sh:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;#!/bin/bash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# admin node definitions and other slony-related information are&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# stored in our preamble file.  This will define the $PREAMBLE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# environment variable that contains basic information common to all&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# Slony-related scripts, such as slony cluster name, the nodes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# present, and how to reach them to install slony, etc.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    . slony_preamble.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    slonik &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    $PREAMBLE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    init cluster ( id = 1, comment = &amp;#39;A1&amp;#39; );
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=2,  comment = &amp;#39;A2&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=3,  comment = &amp;#39;A3&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=4,  comment = &amp;#39;A4&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=5,  comment = &amp;#39;B1&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=6,  comment = &amp;#39;B2&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=7,  comment = &amp;#39;B3&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=8,  comment = &amp;#39;B4&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=9,  comment = &amp;#39;C1&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=10, comment = &amp;#39;C2&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=11, comment = &amp;#39;C3&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store node (id=12, comment = &amp;#39;C4&amp;#39;, event node=1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # pathways from A1 -&amp;gt; A2, A3, A4 and back
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 1, client = 2, conninfo = &amp;#39;dbname=data host=node2.datacenter-a.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 1, client = 3, conninfo = &amp;#39;dbname=data host=node3.datacenter-a.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 1, client = 4, conninfo = &amp;#39;dbname=data host=node4.datacenter-a.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 2, client = 1, conninfo = &amp;#39;dbname=data host=node1.datacenter-a.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 3, client = 1, conninfo = &amp;#39;dbname=data host=node1.datacenter-a.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 4, client = 1, conninfo = &amp;#39;dbname=data host=node1.datacenter-a.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # pathway from A1 -&amp;gt; B1 and back
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 1, client = 5, conninfo = &amp;#39;dbname=data host=node1.datacenter-b.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 5, client = 1, conninfo = &amp;#39;dbname=data host=node1.datacenter-a.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # pathways from B1 -&amp;gt; B2, B3, B4 and back
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 5, client = 6, conninfo = &amp;#39;dbname=data host=node2.datacenter-b.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 5, client = 7, conninfo = &amp;#39;dbname=data host=node3.datacenter-b.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 5, client = 8, conninfo = &amp;#39;dbname=data host=node4.datacenter-b.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 6, client = 5, conninfo = &amp;#39;dbname=data host=node1.datacenter-b.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 7, client = 5, conninfo = &amp;#39;dbname=data host=node1.datacenter-b.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 8, client = 5, conninfo = &amp;#39;dbname=data host=node1.datacenter-b.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # pathway from A1 -&amp;gt; C1 and back
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 1, client = 9, conninfo = &amp;#39;dbname=data host=node1.datacenter-c.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 9, client = 1, conninfo = &amp;#39;dbname=data host=node1.datacenter-a.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # pathways from C1 -&amp;gt; C2, C3, C4 and back
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 9, client = 10, conninfo = &amp;#39;dbname=data host=node2.datacenter-c.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 9, client = 11, conninfo = &amp;#39;dbname=data host=node3.datacenter-c.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 9, client = 12, conninfo = &amp;#39;dbname=data host=node4.datacenter-c.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 10, client = 9, conninfo = &amp;#39;dbname=data host=node1.datacenter-c.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 11, client = 9, conninfo = &amp;#39;dbname=data host=node1.datacenter-c.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    store path (server = 12, client = 9, conninfo = &amp;#39;dbname=data host=node1.datacenter-c.com&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    EOF&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see in the initialization script, we’re defining the basic
topology for the cluster. We’re defining each individual node, and
the paths that slony will use to communicate events and other status.
Since slony needs to communicate status both ways, we need to define
the paths for each node’s edge both ways. In particular, we’ve
defined pathways from A1 to each of the other A nodes, A1 to B1 and
C1, and B1 and C1 to each of their respective nodes.&lt;/p&gt;
&lt;p&gt;Now it’s a matter of defining the replication sets and describing the
subscriptions for each. We will use something like the following for
our script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cluster_define_set1.sh:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;#!/bin/bash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# reusing our standard cluster information&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    . slony_preamble.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    slonik &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    $PREAMBLE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    create set ( id = 1, origin = 1, comment = &amp;#39;set 1&amp;#39; );
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    set add table ( set id = 1, origin = 1, id = 1, fully qualified name = &amp;#39;public.table1&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    set add table ( set id = 1, origin = 1, id = 2, fully qualified name = &amp;#39;public.table2&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    set add table ( set id = 1, origin = 1, id = 3, fully qualified name = &amp;#39;public.table3&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    EOF&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we’ve defined the tables that we want replicated from A1 to the
entire cluster; there is nothing specific to this particular scenario
that we need to consider.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cluster_subscribe_set1.sh:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;#!/bin/bash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# reusing our standard cluster information&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    . slony_preamble.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    slonik &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    $PREAMBLE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # define our forwarding subscriptions (i.e., A1 -&amp;gt; B1, C1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 1, receiver = 5, forward = yes);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 1, receiver = 9, forward = yes);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # define the subscriptions for each of the datacenter sets
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # A1 -&amp;gt; A2, A3, A4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 1, receiver = 2, forward = no);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 1, receiver = 3, forward = no);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 1, receiver = 4, forward = no);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # B1 -&amp;gt; B2, B3, B4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 5, receiver = 6, forward = no);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 5, receiver = 7, forward = no);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 5, receiver = 8, forward = no);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    # C1 -&amp;gt; C2, C3, C4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 9, receiver = 10, forward = no);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 9, receiver = 11, forward = no);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    subscribe set ( id = 1, provider = 9, receiver = 12, forward = no);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    EOF&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The key points here are that you specify the provider nodes and the
receiver nodes to specify how the particular replication occurs. For
the subscription to any cascade point (i.e., B1 and C1), you need to
have the ‘forward = yes’ parameter to ensure that the events properly
cascade to the sub-nodes. In any of the other nodes’ subscription,
you should set ‘forward = no’.&lt;/p&gt;
&lt;p&gt;In actual deployment of this setup, you would want to wait for the
subscription from A1 -&amp;gt; B1 and A1 -&amp;gt; C1 to complete successfully
before subscribing the sub-nodes. Additionally, this solution assumes
high availability between nodes and does not address failure of
particular machines; in particular, A1, B1, and C1 are key to
maintaining the full replication.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Common Topics in Scalability</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/01/common-topics-in-scalability/"/>
      <id>https://www.endpointdev.com/blog/2010/01/common-topics-in-scalability/</id>
      <published>2010-01-04T00:00:00+00:00</published>
      <author>
        <name>Ethan Rowe</name>
      </author>
      <content type="html">
        &lt;p&gt;It rarely makes sense for a startup business to tackle scalability questions from the outset, because it raises the cost and complexity of development and operational support, while solving problems that a start-up business doesn’t typically yet need solved (i.e. handling tons of users). From a cost-effectiveness perspective, it makes sense to solve these problems when the need is actually evident.&lt;/p&gt;
&lt;p&gt;That said, systems can be designed with scalability in mind such that the system easily lends itself to incremental changes that scale up its capacity.&lt;/p&gt;
&lt;p&gt;The basic principles and techniques that typically come up in scalability discussions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;horizontal scalability&lt;/strong&gt;: a particular component is “horizontally scalable” if you can deploy redundant instances of that component in parallel; this is the ultimate scalability win, as it means you can readily add raw processing power for that component at low cost.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;vertical scalability&lt;/strong&gt;: the practice of increasing a single component’s power/capacity to improve performance/throughput is referred to as “vertically scaling” that component. From the layperson’s perspective, this is the most easily-understood technique, as it effectively amounts to buying a faster server, adding RAM to an existing server, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;caching&lt;/strong&gt;: caching can be about raw speed, but is more important from a scalability perspective; caches reduce the overall work a system needs to do for any given request, and can simplify the overall request to entirely eliminate the overhead of multiple-component involvement (for instance, a cache in the application layer can potentially eliminate any calls to the database for a given request)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Horizontal scalability brings the biggest win in terms of potential system capacity, as it allows you to handle increased demand (whether that demand is visitors, orders, etc.) through the simple means of adding more servers to your infrastructure. Virtualization and, its logical result, cloud hosting make this kind of infrastructure expansion simpler and often more cost-effective than ever before.&lt;/p&gt;
&lt;p&gt;Some examples of horizontal scalability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Running your appserver (Rails, Django, etc.) on multiple servers, with a load balancer distributing traffic more-or-less evenly across those servers&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The entire application tier is scaled horizontally and can expand/contract as needed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Session management becomes an issue; sessions either get moved to a shared component like the database, or server affinity techniques are used with the load balancer such that a single client/user always hits the same application server, so sessions can be file-based on each app server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The database likely becomes the bottleneck for system capacity and performance.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Master/slave database replication, with multiple slave databases fronted by a load balancer distributing database reads across the slaves.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Database reads, which in typical webapps account for the bulk of database queries, can be spread across multiple servers and are thus scaled horizontally.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The total number of slave databases is likely limited by the capacity of the master; each additional slave adds some overhead to the master, so diminishing returns eventually kick in.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The ease with which a given component can be scaled horizontally largely comes down to how it manages state, or, in other words: how it manages the data that it works with.&lt;/p&gt;
&lt;p&gt;Application servers are generally designed to be “stateless”, which effectively means that the response for a given request is not dependent on a previous request (though the idea of the “session” is obviously a big exception here). Due to this stateless nature, it’s usually cheap and easy to run your application server in a horizontally-scalable configuration.&lt;/p&gt;
&lt;p&gt;By contrast, relational databases are all about state: the entire point of the database is to act as the arbiter of state, maintain that data on disk, and answer questions about that data. We typically expect the database to tell the truth and give a consistent answer about each piece of data. The consistency expectation leads to the need for each piece of data to have one and only one canonical location, which means it cannot be scaled across multiple servers. You can scale &lt;em&gt;copies&lt;/em&gt; of data across multiple servers (as done in master/slave replication), but the True Value for a bit of state has to live in one place. Therefore, master databases are generally cut off from the glories of horizontal scalability. (Note: “sharding” offers a way to scale writes, but it doesn’t make your database literally horizontally scalable; any given datum still has one canonical location which is limited to vertical scalability per usual).&lt;/p&gt;
&lt;p&gt;Enter caching. When you cannot horizontally scale a given component, you can instead store and reuse copies of the results of that component’s operations, to reduce the overall work done by that component. Most modern application server frameworks provide a variety of helpful caching tools right out of the box, and a good cache strategy can squeeze a great deal of throughput out of a simple architecture. However, caching is bigger than the app tier, as the examples show:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HTTP caching&lt;/strong&gt;: HTTP clients (i.e. web browsers) can cache the resources they request from your system, if you give them the proper instructions on how to do so (via various HTTP headers). At a minimum, browsers ought to be able to cache the images, CSS, and JavaScript files that make up your site’s large scale visual design, which means they don’t need to request those files repeatedly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HTTP caching redux&lt;/strong&gt;: HTTP caching reverse proxies (Varnish, Squid, etc.) can sit between your web/app tier and your users’ browsers, and can cache resources from your site based on HTTP headers and other configurable aspects; when users request a resource, they hit the HTTP reverse proxy first, and if the resource is available in the cache, the cached version is used. This means the user gets a faster response and your actual application does less work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Page caching&lt;/strong&gt;: By caching full copies of your dynamically-generated resources (web pages), your system can see enormous scalability gains. This can fall logically under HTTP caching or under application-tier caching or somewhere between; the important point is to consider the idea that you cache an entire dynamic page in some manner, as it brings both such big performance wins and potential design complexities/constraints.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Application tier caching&lt;/strong&gt;: using simple file-based caches, or scalable, shared, distributed cache layers like memcached or redis, your application can cache the results of expensive queries, frequently-used operations/widgets, etc. and thus reuse the fruits of its own labors; this can reduce the computational cost for handling any given request and thus improve both raw request speed and overall throughput (scalability).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Database replication&lt;/strong&gt;: though not typically referred to as “caching”, the classic master/slave database replication strategy is effectively a very fancy cache. By pushing out copies of data at near-real-time, this lets your master database server do less work while still giving your application servers highly accurate results.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Controlled denormalization&lt;/strong&gt;: within your database, you can use triggers and such to manage denormalized data, allowing frequently-used or expensive calculations to be cached as part of the database schema. This allows the application to rely upon such calculations at lower cost. Materialized views fit within this category.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These caching examples/categories vary in complexity of implementation, but they share a common principle: increase system capacity by reusing the system’s work. The use of caching naturally involves trade-offs, but in the simple case, a straightforward expiry-based cache can have a dramatic impact on performance. A sluggish webapp can get a second wind by wrapping the most common database-driven components within a timed cache. For new development, for which your caching strategy can factor in at design time, caching can yield great performance/scalability with extremely high accuracy. In particular, the refresh-on-write strategy (in which the code paths in your app responsible for changing state are also responsible for updating related caches) can be a huge scalability win (this was exactly the strategy we used—​with great results—​for Backcountry.com’s SteepandCheap site in fall of 2007 and the initial launch of their product Q&amp;amp;A features in early 2008).&lt;/p&gt;
&lt;p&gt;Ideally, a good caching strategy does not merely reuse the results of earlier work, but in fact cuts out calls to other services; good caching in the application tier may mean that most requests do not need to involve the database at all; good HTTP caching techniques cut down the number of requests that involve your application server. And so on.&lt;/p&gt;
&lt;p&gt;Beyond these topics, scalability benefits can frequently come from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;query optimization&lt;/strong&gt;: poor database usage can result in slow page load times, excess I/O, and can overload the database unnecessarily; query optimization can have a significant impact there. This is rather like a corollary to vertically scaling your database: your database scales better relative to the hardware because its demands on the hardware are reduced.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;data management optimization&lt;/strong&gt;: big scalability gains can often come from revising the structure of the data involved at the scalability pinch points; for instance, having a single inventory count for a given SKU is a scalability bottleneck compared to having an inventory item record per 1 inventory count per sku. The former results in lock contention in high-order-volume situations, while the latter can minimize such locking and prevent one user’s order from blocking another.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;application database usage&lt;/strong&gt;: related to query optimization is the issue of how the application structures its queries. In an age where people increasingly interact with their datasources through an object-relational mapper (ORM), one commonly finds cases in which the application issues per-record queries within a loop, meaning that a set of &lt;em&gt;N&lt;/em&gt; records yields &lt;em&gt;N+1&lt;/em&gt; queries (1 query for the initial set, 1 query per iteration). This kind of database usage brings unnecessary I/O overhead and leads to sluggish app performance; rewrite the queries or tweak the ORM as necessary such that the first query loads all the data, or that a second query fetches all the extra data for all members of the first query’s result set (ORMs often will do the second strategy for you if you let them know what you want).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The unfortunately-named “NoSQL” movement has a lot of exciting possibilities for systems that need to handle large volumes of data, or scale out to very high write volumes (I find both Cassandra and HBase to be particularly interesting). However, unless you know from the outset that your dataset size or write volume will be pushing the limits of the traditional relational database, or that you need distributed operations for availability purposes, the NoSQL offerings are quite possibly counterproductive.&lt;/p&gt;
&lt;p&gt;These solutions typically offer some particular advantage but with a trade-off of some kind; for small businesses with a rapidly-evolving sense of self and the problem space, the traditional RDBMS brings a huge amount of flexibility in how one works with data, as well as a well-understood operational paradigm (for high availability, reliability, backups, etc.). The “Big Data” benefits of NoSQL probably don’t apply for such systems and the ease with which RDBMSes handle arbitrarily-complex queries allow the growing business/system to develop iteratively and arrive at a better understanding of the true underlying needs. The trade-offs and such are beyond the scope of these musings, so I just won’t give them further consideration here; however, the NoSQL ecosystem is good to know about and certainly can factor into scalability discussions depending on your use cases. Several of them may in fact fit well within a caching strategy, rather than as an authoritative datasource.&lt;/p&gt;
&lt;p&gt;Here are some general rules of thumb I would recommend if you want to be ready to scale up when the need arises (as is the whole point of these ramblings):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Write well-organized code&lt;/em&gt;: Maintain separation of concerns in your application code, maximize code reuse, and keep your classes, modules, functions, etc. limited in their scope. This keeps each individual code piece simple, maximizes the ease with which things like caching can be introduced, and minimizes the code paths that have to be changed in order to optimize the performance of any given component.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Keep your canonical data normalized&lt;/em&gt;: This could probably be stated instead as “model your data relationally”, but that assumes an RDBMS, which isn’t necessarily your only datastore. In any case, normalized data is easier to manage over the life of a system, and just as better-organized code is more-easily optimized for performance/scalability than poorly-organized code, well-organized data is more-easily rearranged or cached, etc. Introduce denormalization in a targeted, needs-based manner, but don’t let that denormalization ever be considered part of your canonical dataset; denormalized data is broken data that you should be able to throw out and rebuild at any time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Avoid session usage&lt;/em&gt;: Sessions are handy, but they in fact violate the statelessness of the application server, introduce one point of complexity (though a manageable point) for horizontal scaling, potentially introduce database scaling problems (if you’re using database-backed sessions), etc. If the state matters enough to keep it from one request to the next, consider modeling it and storing it properly in the database. For state that just can’t go in the database and you just can’t live without&amp;hellip;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Use the client&lt;/em&gt;: The majority of clients for the majority of webapps are web browsers. Those web browsers typically have storage (cookies for now, and more options coming) and frequently have programming capacity (JavaScript). Use it! In particular, cookies can take the role of the user session, and potentially eliminate (or at least reduce) the need for server-side session state. Work done by the client is work not done by your servers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Think RESTfully&lt;/em&gt;: While you don’t necessarily need to literally design a RESTful application, be mindful of the RESTful design principles and keep your URLs well-designed. Critically, don’t allow the same URL to front wildly-varying resources; such resources are the bane of page/HTTP caching.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Be mindful of user-specific resources&lt;/em&gt;: Any resource, document, etc. that you serve with user (or session) specific information therein is a resource that is potentially more difficult to cache effectively. Consider crafting your URLs such that the URL is per-user, if that can fit your problem-space effectively (this allows HTTP or page-level caching to still be an option). If common resources (like a “home page”) need to show per-user details, consider using the client (cookies, JavaScript) to encapsulate the user-specific stuff such that the common resource itself is identical for all users (and thus also remains open to HTTP or page-level caching).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

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