<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Padraig O'Sullivan - Drupal</title>
    <description>Posts categorized as 'drupal'</description>
    <link>http://posulliv.github.com</link>
    <atom:link href="http://posulliv.github.com/drupal.xml" rel="self" type="application/rss+xml" />
    
      
        
          <item>
            <title>Local Development with Ariadne</title>
            <description>&lt;p&gt;I recently started a new development position with &lt;a href='http://www.blinkreaction.com/'&gt;Blink Reaction&lt;/a&gt; so I needed to get somewhat serious about setting up a local Drupal development environment.&lt;/p&gt;

&lt;h2 id='ariadne'&gt;Ariadne&lt;/h2&gt;

&lt;p&gt;I was leaning towards making use of &lt;a href='http://www.vagrantup.com/'&gt;Vagrant&lt;/a&gt; for managing local development environments so I can easily switch between different projects or branches. I also believe Vagrant makes it easier to have as close a mirror to production locally as possible.&lt;/p&gt;

&lt;p&gt;I discovered a very interesting project from &lt;a href='http://myplanetdigital.com/'&gt;MyPlanet Digital&lt;/a&gt; named &lt;a href='https://github.com/myplanetdigital/vagrant-ariadne'&gt;vagrant-ariadne&lt;/a&gt;. Ariadne is a customized implementation of Vagrant and allows for easy deployment of Drupal installation profiles to a local VM. Another nice feature is that it attempts to emulate Acquia&amp;#8217;s infrastructure. This is useful as a lot of Blink&amp;#8217;s clients are deployed on the Acquia Cloud.&lt;/p&gt;

&lt;p&gt;Assuming you have Vagrant, rvm and a ruby environment installed on your workstation, installing Ariadne is pretty straightforward:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;j&lt;/span&gt;
&lt;span class='go'&gt;vagrant gem install vagrant-vbguest vagrant-hostmaster vagrant-librarian&lt;/span&gt;
&lt;span class='go'&gt;[sudo] gem install librarian rake knife-solo&lt;/span&gt;
&lt;span class='go'&gt;git clone https://github.com/myplanetdigital/vagrant-ariadne.git&lt;/span&gt;
&lt;span class='go'&gt;cd vagrant-ariadne&lt;/span&gt;
&lt;span class='go'&gt;bundle install&lt;/span&gt;
&lt;span class='go'&gt;bundle exec rake setup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Everything is now configured to boot a virtual box. Ariadne comes with a simple example that can be deployed:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;j&lt;/span&gt;
&lt;span class='go'&gt;project=example vagrant up&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once that command finishes running, the site can be viewed at &lt;code&gt;http://example.dev/&lt;/code&gt; (Ariadne uses &lt;a href='https://github.com/mosaicxm/vagrant-hostmaster'&gt;vagrant-hostmaster&lt;/a&gt; for managing &lt;code&gt;/etc/hosts&lt;/code&gt; entries).&lt;/p&gt;

&lt;p&gt;A more involved cookbook is a cookbook for deploying the &lt;a href='https://github.com/wet-boew/wet-boew-drupal'&gt;Web Experience Toolkit&lt;/a&gt; available on &lt;a href='https://github.com/patcon/ariadne-wet-boew-drupal'&gt;github also&lt;/a&gt;. If we wanted to deploy the master branch of this site, we could do:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;
bundle exec rake &amp;quot;init_project[https://github.com/wet-boew/ariadne-wet-boew-drupal]&amp;quot;
project=wet-boew-drupal branch=master vagrant up
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And that&amp;#8217;s it!&lt;/p&gt;

&lt;p&gt;Another nice feature of these deployed environments is that they are configured to allow remote debugging (relevant when setting up an IDE as mentioned later) and the actual site code is shared as an NFS mount. For example, the contents of my &lt;code&gt;/etc/exports&lt;/code&gt; file after booting a box with Ariadne looks like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;#&lt;/span&gt; VAGRANT-BEGIN: 7ac1cf50-4498-4e49-bd66-edac4a9b2d7e
&lt;span class='go'&gt;&amp;quot;/Users/posullivan/vagrant-ariadne/tmp/apt/cache&amp;quot; 33.33.33.10 -mapall=501:20&lt;/span&gt;
&lt;span class='go'&gt;&amp;quot;/Users/posullivan/vagrant-ariadne/tmp/drush/cache&amp;quot; 33.33.33.10 -mapall=501:20&lt;/span&gt;
&lt;span class='go'&gt;&amp;quot;/Users/posullivan/vagrant-ariadne/data/html&amp;quot; 33.33.33.10 -mapall=501:20&lt;/span&gt;
&lt;span class='gp'&gt;#&lt;/span&gt; VAGRANT-END: 7ac1cf50-4498-4e49-bd66-edac4a9b2d7e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thus, if I navigate to the &lt;code&gt;~/vagrant-ariadne/data/html&lt;/code&gt; directory or import that in my IDE, I can edit the code deployed on the vagrant box.&lt;/p&gt;

&lt;h3 id='drupal_core_from_git'&gt;Drupal Core from git&lt;/h3&gt;

&lt;p&gt;Another use I&amp;#8217;ve found for ariadne is building a local environment for the latest drupal core. To accomplish this, I created a role file named &lt;code&gt;roles/core.rb&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;name&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;core&amp;quot;&lt;/span&gt;
&lt;span class='n'&gt;description&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Install requirements to run Drupal core.&amp;quot;&lt;/span&gt;
&lt;span class='n'&gt;run_list&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;recipe[mysql::server]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;recipe[mysql::client]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;recipe[php::module_mysql]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;recipe[php::module_curl]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;recipe[php::module_gd]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;recipe[php::module_apc]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;recipe[drush::utils]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;recipe[drush::make]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;recipe[php::write_inis]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='n'&gt;default_attributes&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
  &lt;span class='ss'&gt;:drush&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='ss'&gt;:version&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;5.8.0&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='ss'&gt;:mysql&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='ss'&gt;:server_debian_password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;root&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='ss'&gt;:server_root_password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;root&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='ss'&gt;:server_repl_password&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;root&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='ss'&gt;:bind_address&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;127.0.0.1&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='ss'&gt;:tunable&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='ss'&gt;:key_buffer&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;384M&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='ss'&gt;:table_cache&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;4096&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='p'&gt;},&lt;/span&gt;
  &lt;span class='p'&gt;},&lt;/span&gt;
&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;j&lt;/p&gt;

&lt;p&gt;Next, I created a new cookbook project named &lt;code&gt;core&lt;/code&gt; and created a simple &lt;code&gt;default.rb&lt;/code&gt; recipe for this cookbook. This recipe looks like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;branch&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;ariadne&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;][&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;branch&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;

&lt;span class='n'&gt;git&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;/mnt/www/html/drupal&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;user&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;vagrant&amp;quot;&lt;/span&gt;
  &lt;span class='n'&gt;repository&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://git.drupal.org/project/drupal.git&amp;quot;&lt;/span&gt;
  &lt;span class='n'&gt;reference&lt;/span&gt; &lt;span class='n'&gt;branch&lt;/span&gt;
  &lt;span class='n'&gt;enable_submodules&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;
  &lt;span class='n'&gt;action&lt;/span&gt; &lt;span class='ss'&gt;:sync&lt;/span&gt;
  &lt;span class='n'&gt;notifies&lt;/span&gt; &lt;span class='ss'&gt;:run&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;bash[Installing Drupal...]&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:immediately&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;bash&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Installing Drupal...&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;user&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;vagrant&amp;quot;&lt;/span&gt;
  &lt;span class='n'&gt;group&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;vagrant&amp;quot;&lt;/span&gt;
  &lt;span class='n'&gt;code&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class='no'&gt;EOH&lt;/span&gt;
&lt;span class='sh'&gt;    drush -y si \&lt;/span&gt;
&lt;span class='sh'&gt;      --root=/mnt/www/html/drupal \&lt;/span&gt;
&lt;span class='sh'&gt;      --db-url=mysqli://root:root@localhost/drupal \&lt;/span&gt;
&lt;span class='sh'&gt;      --site-name=&amp;quot;Drupal Core Installed from Git&amp;quot; \&lt;/span&gt;
&lt;span class='sh'&gt;      --site-mail=vagrant+site@localhost \&lt;/span&gt;
&lt;span class='sh'&gt;      --account-mail=vagrant+admin@locahost \&lt;/span&gt;
&lt;span class='sh'&gt;      --account-name=admin \&lt;/span&gt;
&lt;span class='sh'&gt;      --account-pass=admin&lt;/span&gt;
&lt;span class='no'&gt;  EOH&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;site&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;ariadne&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;][&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;host_name&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;nil?&lt;/span&gt; &lt;span class='p'&gt;?&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;ariadne&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;][&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;project&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;.dev&amp;quot;&lt;/span&gt; &lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;ariadne&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;][&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;host_name&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;

&lt;span class='n'&gt;web_app&lt;/span&gt; &lt;span class='n'&gt;site&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;cookbook&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;ariadne&amp;quot;&lt;/span&gt;
  &lt;span class='n'&gt;template&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;drupal-site.conf.erb&amp;quot;&lt;/span&gt;
  &lt;span class='n'&gt;port&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;apache&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;][&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;listen_ports&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;to_a&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='n'&gt;server_name&lt;/span&gt; &lt;span class='n'&gt;site&lt;/span&gt;
  &lt;span class='n'&gt;server_aliases&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;www.&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;site&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='n'&gt;docroot&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;/mnt/www/html/drupal&amp;quot;&lt;/span&gt;
  &lt;span class='n'&gt;notifies&lt;/span&gt; &lt;span class='ss'&gt;:reload&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;service[apache2]&amp;quot;&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With all of the above in place, its quite simple to create a local VM based on the latest in the &lt;code&gt;7.x&lt;/code&gt; branch of drupal core:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='nv'&gt;project&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;core &lt;span class='nv'&gt;branch&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;7.x vagrant up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above command simply needs to have the branch name modified to deploy a different branch. Once the above command completes, a site will be available at &lt;code&gt;core.dev&lt;/code&gt; and I can log in as the &lt;code&gt;admin&lt;/code&gt; user using the credentials specified in my cookbook.&lt;/p&gt;

&lt;h3 id='private_repositories'&gt;Private Repositories&lt;/h3&gt;

&lt;p&gt;Most repositories for client projects are stored in private repositories. Thankfully, thats not an issue with ariadne. Ariadne uses agent forwarding to forward the host machine&amp;#8217;s ssh session into the VM, including keys and passphrases stored by ssh-agent. What this means is that your VM will have the same Git/SSH access that you enjoy on your local machine. I&amp;#8217;ve not had a problem checking out code stored in private repositories on bitbucket for example.&lt;/p&gt;

&lt;h2 id='ide'&gt;IDE&lt;/h2&gt;

&lt;p&gt;For an IDE, I&amp;#8217;ve been an Eclipse user in the past for Java projects I&amp;#8217;ve worked on so &lt;a href='http://www.aptana.org/'&gt;Aptana&lt;/a&gt; seemed like a good fit for my needs at the moment. A few &lt;a href='http://www.pixelite.co.nz/article/configuring-aptana-drupal-development'&gt;existing&lt;/a&gt; &lt;a href='http://knackforge.com/blog/vannia/setting-aptana-studio-3-ide-drupal-development'&gt;articles&lt;/a&gt; already exist on configuring Aptana for Drupal development so I&amp;#8217;m not going to go into too much details here.&lt;/p&gt;

&lt;p&gt;Installation is very straightforward with the binary downloaded from the &lt;a href='http://www.aptana.org/'&gt;site&lt;/a&gt;. A ruble exists for Drupal so its pretty natural to install that:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;git clone git://github.com/arcaneadam/Drupal-Bundle-for-Aptana.git ~/Documents/Aptana Rubles/Drupal-Bundle-for-Aptana&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next item is to configure Aptana to adhere to the Drupal coding standards. I used an &lt;a href='https://github.com/fxarte/Aptana-Drupal-PHP.profile'&gt;existing profile for Aptana&lt;/a&gt; that could be imported for this.&lt;/p&gt;

&lt;p&gt;The final thing I needed to configure was a debug configuration. To do this, I created a new PHP web page configuration. First, a new PHP server needs to be added. In this example, lets assume I am using the example box I mentioned in the Ariadne section whose hostname is &lt;code&gt;example.dev&lt;/code&gt;. The web server configuration dialog when configured with this hostname and appropriate directory for the site root looks like:&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/aptana_first.png' alt='image' /&gt;&lt;/p&gt;

&lt;p&gt;Once a PHP server has been added, the rest of the information to fill in for the debug configuration is pretty straightforward as shown below:&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/aptana_second.png' alt='image' /&gt;&lt;/p&gt;

&lt;p&gt;I like to select the break at first line option to make sure the debug configuration works correctly.&lt;/p&gt;

&lt;p&gt;With this in place, any visit to &lt;code&gt;example.dev&lt;/code&gt; will result in the breakpoint being hit.&lt;/p&gt;

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

&lt;p&gt;I&amp;#8217;ve still not settled on this combination for my development environment but I was definitely pretty excited upon discovering the Ariadne project. The drawbacks that I see to using Ariadne are: 1) the need to create a cookbook for each project you want to work with, 2) the project is still in beta stage so documentation is fairly lacking (fair enough for a beta project though), and 3) if you are not familiar with &lt;a href='http://www.opscode.com/chef/'&gt;chef&lt;/a&gt;, using Ariadne may prove challenging (although it provides the perfect excuse to become familiar with chef).&lt;/p&gt;

&lt;p&gt;PHPStorm is the IDE that seems to be pretty popular when I ask what other people are using for an editor but given there is a license fee associated with it, I didn&amp;#8217;t want to splurge on that just yet. Aptana looks to work just fine for me and satisfies my needs nicely.&lt;/p&gt;</description>
            <pubDate>Mon, 13 May 2013 00:00:00 -0700</pubDate>
            <link>http://posulliv.github.com/2013/05/13/ariadne</link>
            <guid isPermaLink="true">http://posulliv.github.com/2013/05/13/ariadne</guid>
          </item>
        
      
    
      
        
          <item>
            <title>Akiban is Now Open Source</title>
            <description>&lt;p&gt;I&amp;#8217;ve written a lot about the work I do for &lt;a href='http://akiban.com/'&gt;Akiban&lt;/a&gt; with Drupal in the past and many people would ask if Akiban was open source software. Well in the last few weeks we actually open sourced our &lt;a href='http://github.com/akiban/akiban-server'&gt;database server&lt;/a&gt;. We also have &lt;a href='http://software.akiban.com/releases/1.6.0/installers/'&gt;downloads&lt;/a&gt; for various platforms such as Windows and OSX besides binary packages for Linux variants.&lt;/p&gt;

&lt;p&gt;I &lt;a href='http://posulliv.github.com/2012/12/14/drupal-7-install-akiban/'&gt;wrote previously&lt;/a&gt; about how to install Drupal 7 completely on &lt;a href='http://akiban.com/'&gt;Akiban&lt;/a&gt;. You can still follow that post to get up and running except now there is a tiny change for our public repositories:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo apt-get install -y python-software-properties&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 0AA4244A&lt;/span&gt;
&lt;span class='go'&gt;sudo add-apt-repository &amp;quot;deb http://software.akiban.com/apt-public/ lucid main&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get update&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get install -y akiban-server postgresql-client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some of the things included in our open source database are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://docs.akiban.org/en/latest/service/spatial.html'&gt;spatial indexes&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://docs.akiban.org/en/latest/service/fulltext.html'&gt;full text indexes&lt;/a&gt; (implemented using Lucene)&lt;/li&gt;

&lt;li&gt;&lt;a href='https://akiban.readthedocs.org/en/latest/service/restapireference.html'&gt;REST access&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='https://akiban.readthedocs.org/en/latest/quickstart/nested.html'&gt;nested SQL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are also working on offering on a service offering for our database server so there will be no need to manage an installation yourself. If you are interested in trying our service in its current beta form, please let me know in the comments or hit me up on &lt;a href='https://twitter.com/intent/user?screen_name=posulliv'&gt;twitter&lt;/a&gt; and I&amp;#8217;d be happy to hook you up or just visit our &lt;a href='http://akiban.com/'&gt;website&lt;/a&gt;. We also have a &lt;a href='https://groups.google.com/a/akiban.com/d/forum/akiban-user'&gt;public mailing list&lt;/a&gt; for the Akiban project if you try anything out and have any questions.&lt;/p&gt;</description>
            <pubDate>Tue, 02 Apr 2013 00:00:00 -0700</pubDate>
            <link>http://posulliv.github.com/2013/04/02/akiban-open-source</link>
            <guid isPermaLink="true">http://posulliv.github.com/2013/04/02/akiban-open-source</guid>
          </item>
        
      
    
      
        
          <item>
            <title>Akiban as a MySQL Replica with Drupal 7</title>
            <description>&lt;p&gt;I &lt;a href='http://posulliv.github.com/2012/12/14/drupal-7-install-akiban/'&gt;previously wrote&lt;/a&gt; about how to install Drupal 7 completely on &lt;a href='http://akiban.com/'&gt;Akiban&lt;/a&gt;. However, this is not how our current customers are using us. The vast majority of all Drupal installations currently run on MySQL. What we at Akiban are currently aiming to do is to be deployed as a regular MySQL slave and if there are any queries that are problematic for MySQL, we work with customers to make sure those queries get executed by Akiban (and with a significant performance improvement).&lt;/p&gt;

&lt;p&gt;In this post, I wanted to cover how to setup Akiban as a MySQL slave and how a query is typically re-directed to an Akiban server from Drupal. This article is specific to Drupal 7.&lt;/p&gt;

&lt;p&gt;First, I setup a regular Drupal install on Ubuntu 12.04 with MySQL 5.5.28. This is going to serve as the master server. To configure replication in MySQL is pretty &lt;a href='http://dev.mysql.com/doc/refman/5.5/en/replication-howto.html'&gt;straightforward&lt;/a&gt;. The following needs to be placed in your &lt;code&gt;my.cnf&lt;/code&gt; file and MySQL needs to be re-started:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;log-bin=mysql-bin&lt;/span&gt;
&lt;span class='go'&gt;server-id=11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A user needs to be created for replication:&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;CREATE USER &amp;#39;repl&amp;#39;@&amp;#39;%&amp;#39; IDENTIFIED BY &amp;#39;password&amp;#39;;&lt;/span&gt;
&lt;span class='go'&gt;GRANT REPLICATION SLAVE ON *.* TO &amp;#39;repl&amp;#39;@&amp;#39;%&amp;#39;;&lt;/span&gt;
&lt;span class='go'&gt;FLUSH PRIVILEGES;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;Next steps are to take a consistent snapshot of your Drupal schema with &lt;code&gt;mysqldump&lt;/code&gt; and capture the output of &lt;code&gt;SHOW MASTER STATUS&lt;/code&gt; to get the appropriate binlog co-ordinates.&lt;/p&gt;

&lt;p&gt;Next, we need to setup an Akiban MySQL slave. We will use an entirely separate instance for this purpose. First, the software to install on this slave is:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo apt-get install -y mysql-client mysql-server&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get install -y python-software-properties&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 0AA4244A&lt;/span&gt;
&lt;span class='go'&gt;sudo add-apt-repository &amp;quot;deb http://software.akiban.com/apt-developer/lucid main&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get update&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get install -y akiban-server akiban-adapter-mysql postgresql-client&lt;/span&gt;
&lt;span class='go'&gt;echo &amp;quot;INSTALL PLUGIN akibandb SONAME &amp;#39;libakibandb_engine.so&amp;#39;&amp;quot; | mysql -u&lt;/span&gt;
&lt;span class='go'&gt;root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Issuing the &lt;code&gt;SHOW PLUGINS&lt;/code&gt; command on this slave will now show the &lt;code&gt;AkibanDB&lt;/code&gt; storage engine. The next step is to now import the &lt;code&gt;mysqldump&lt;/code&gt; file taken from the master and configure replication. On the slave server, you need to make sure &lt;code&gt;server-id&lt;/code&gt; is set in the &lt;code&gt;my.cnf&lt;/code&gt; file. Then to enable replication, a &lt;code&gt;CHANGE MASTER&lt;/code&gt; command needs to be issued. An example of what that command might look like is:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;CHANGE MASTER TO&lt;/span&gt;
&lt;span class='go'&gt;  MASTER_HOST = &amp;#39;ec2-23-20-112-161.compute-1.amazonaws.com&amp;#39;,&lt;/span&gt;
&lt;span class='go'&gt;  MASTER_USER = &amp;#39;repl&amp;#39;,&lt;/span&gt;
&lt;span class='go'&gt;  MASTER_PASSWORD = &amp;#39;password&amp;#39;,&lt;/span&gt;
&lt;span class='go'&gt;  MASTER_LOG_FILE = &amp;#39;mysql-bin.000001&amp;#39;,&lt;/span&gt;
&lt;span class='go'&gt;  MASTER_LOG_POS = 403&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, issuing &lt;code&gt;START SLAVE&lt;/code&gt; starts up replication. The observant among you will notice all tables are still InnoDB on the slave. We have done nothing to convert any tables to Akiban yet. Before we get to that I want to configure Drupal running on the master server to know about the Akiban slave so it can send queries to it. First, we need to install the &lt;a href='http://drupal.org/sandbox/posulliv/1835778'&gt;Akiban database module&lt;/a&gt; in Drupal (the akiban directory should be copied to whatever the appropriate location for your Drupal install is) and the PHP client drivers for PostgreSQL:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo apt-get install -y git php5-pgsql&lt;/span&gt;
&lt;span class='go'&gt;git clone http://git.drupal.org/sandbox/posulliv/1835778.git akiban&lt;/span&gt;
&lt;span class='go'&gt;cd akiban&lt;/span&gt;
&lt;span class='go'&gt;git checkout 7.x&lt;/span&gt;
&lt;span class='go'&gt;cd ../&lt;/span&gt;
&lt;span class='go'&gt;sudo cp -R akiban /var/www/drupal/includes/database/.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, the &lt;code&gt;settings.php&lt;/code&gt; file needs to be updated to know about this Akiban server:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='php'&gt;&lt;span class='x'&gt;$databases = array (&lt;/span&gt;
&lt;span class='x'&gt;  &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;  array (&lt;/span&gt;
&lt;span class='x'&gt;    &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;    array (&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;database&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;username&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;password&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;localhost&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;port&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;mysql&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;prefix&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;    ),&lt;/span&gt;
&lt;span class='x'&gt;    &amp;#39;slave&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;    array (&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;database&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;username&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;password&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;ec2-23-22-113-161.compute-1.amazonaws.com&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;port&amp;#39; =&amp;gt; &amp;#39;15432&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;akiban&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;prefix&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;    ),&lt;/span&gt;
&lt;span class='x'&gt;  ),&lt;/span&gt;
&lt;span class='x'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I would suggest enabling query logging on the Akiban server so you can see read queries being sent to the slave. Query logging can be enabled by modifying the &lt;code&gt;/etc/akiban/config/server.properties&lt;/code&gt; file to have these entries:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;akserver.querylog.enabled=true&lt;/span&gt;
&lt;span class='go'&gt;akserver.querylog.filename=/var/log/akiban/queries.log&lt;/span&gt;
&lt;span class='go'&gt;akserver.querylog.exec_time_threshold=0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All queries issued against Akiban will now be logged to the &lt;code&gt;/var/log/akiban/queries.log&lt;/code&gt; file since we set the query execution time threshold to 0. Akiban needs to re-started for this to take effect.&lt;/p&gt;

&lt;p&gt;By default, very few queries from Drupal core are sent to a slave database. The search module is probably the best module to test with to see queries being sent to Akiban. The search module can be accessed from your Drupal site by going to &lt;code&gt;http://your.ip.address/drupal/?q=search&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First, we need to convert those tables to Akiban, otherwise any search will now fail since no tables have been converted to Akiban yet. To convert these tables to Akiban, we simply issue the following in MySQL:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sql'&gt;&lt;span class='n'&gt;STOP&lt;/span&gt; &lt;span class='n'&gt;SLAVE&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='k'&gt;ALTER&lt;/span&gt; &lt;span class='k'&gt;TABLE&lt;/span&gt; &lt;span class='n'&gt;search_total&lt;/span&gt; &lt;span class='n'&gt;ENGINE&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;AkibanDB&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='k'&gt;ALTER&lt;/span&gt; &lt;span class='k'&gt;TABLE&lt;/span&gt; &lt;span class='n'&gt;search_index&lt;/span&gt; &lt;span class='n'&gt;ENGINE&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;AkibanDB&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='k'&gt;ALTER&lt;/span&gt; &lt;span class='k'&gt;TABLE&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt; &lt;span class='n'&gt;ENGINE&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;AkibanDB&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='k'&gt;ALTER&lt;/span&gt; &lt;span class='k'&gt;TABLE&lt;/span&gt; &lt;span class='n'&gt;search_index&lt;/span&gt; &lt;span class='k'&gt;ADD&lt;/span&gt; &lt;span class='k'&gt;CONSTRAINT&lt;/span&gt; &lt;span class='o'&gt;`&lt;/span&gt;&lt;span class='n'&gt;__akiban_fk_00&lt;/span&gt;&lt;span class='o'&gt;`&lt;/span&gt; &lt;span class='k'&gt;FOREIGN&lt;/span&gt; &lt;span class='k'&gt;KEY&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;sid&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;REFERENCES&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='k'&gt;ANALYZE&lt;/span&gt; &lt;span class='k'&gt;TABLE&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='k'&gt;ANALYZE&lt;/span&gt; &lt;span class='k'&gt;TABLE&lt;/span&gt; &lt;span class='n'&gt;search_index&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='k'&gt;ANALYZE&lt;/span&gt; &lt;span class='k'&gt;TABLE&lt;/span&gt; &lt;span class='n'&gt;search_total&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='k'&gt;START&lt;/span&gt; &lt;span class='n'&gt;SLAVE&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The relevant tables are now converted to Akiban. Now, try searching content for a keyword. If everything is working correctly, queries should start appearing in the query log on the Akiban server when issuing content searches.&lt;/p&gt;

&lt;p&gt;This is obviously a pretty simple example but now its pretty trivial to send more queries to Akiban. Just change the database target, convert the appropriate tables to Akiban on the slave, and away you go!&lt;/p&gt;

&lt;p&gt;If there is anything you would like more information on, please let me know in the comments or hit me up on &lt;a href='https://twitter.com/intent/user?screen_name=posulliv'&gt;twitter&lt;/a&gt; and I&amp;#8217;d be more than happy to dig in. We also have a &lt;a href='https://groups.google.com/a/akiban.com/d/forum/akiban-user)'&gt;public mailing list&lt;/a&gt; for the Akiban project and I&amp;#8217;d encourage anyone who&amp;#8217;s interested to subscribe to that list and let us know how we&amp;#8217;re doing! Finally, I&amp;#8217;ll be presenting on this topic at &lt;a href='http://drupalcampma.com/how-solve-problem-drupal-queries-akiban'&gt;drupalcamp MA&lt;/a&gt; on January 19th and I am also delivering a joint &lt;a href='http://www.akiban.com/webinars/how-to-ensure-sql-queries-don-t-slow-your-drupal-website#.UPA7B4njktg'&gt;webinar&lt;/a&gt; with Acquia in February on this topic.&lt;/p&gt;</description>
            <pubDate>Fri, 11 Jan 2013 00:00:00 -0800</pubDate>
            <link>http://posulliv.github.com/2013/01/11/akiban-augment</link>
            <guid isPermaLink="true">http://posulliv.github.com/2013/01/11/akiban-augment</guid>
          </item>
        
      
    
      
        
          <item>
            <title>Testing an Alternate Field SQL Storage Module</title>
            <description>&lt;p&gt;After my &lt;a href='http://bit.ly/Wo9BeF'&gt;post yesterday&lt;/a&gt; testing the field storage layer, a commentator pointed out an alternate &lt;a href='http://drupal.org/project/field_sql_norevisions'&gt;SQL storage module&lt;/a&gt; that does not create a revision table for each field. Naturally, I had to try this out to see how what kind of performance was possible with this approach.&lt;/p&gt;

&lt;p&gt;The average throughput numbers I observed using this module are shown in the table below.&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Environment&lt;/th&gt;
    &lt;th&gt;Average Throughput&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Default MySQL&lt;/td&gt;
    &lt;td&gt;2892 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Default PostgreSQL&lt;/td&gt;
    &lt;td&gt;2313 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Tuned MySQL&lt;/td&gt;
    &lt;td&gt;4730 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Tuned PostgreSQL&lt;/td&gt;
    &lt;td&gt;2464 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;The image below shows the results graphically for different environments I tested. The Y axis is throughput (node per minute) with the X axis specifying the CSV file (corresponding to a MLB year) being imported.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/field_norevision_throughput.png' alt='Throughput numbers.' /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;That&amp;#8217;s a pretty big improvement over the numbers I got in my original test. We still are not approaching the 8000 nodes per minute that is possible with a tuned MySQL instance and MongoDB for field storage but at about 5000 nodes per minute, we are getting somewhat close. It does beg the question of whether the performance benefits of MongoDB for field storage are worth it when we can get somewhat close using this module and a site&amp;#8217;s original database system?&lt;/p&gt;

&lt;p&gt;I would be interested in suggestions for read benchmarks from the community for different field storage backends so I can attempt to gain more insight into this question for myself.&lt;/p&gt;</description>
            <pubDate>Tue, 08 Jan 2013 00:00:00 -0800</pubDate>
            <link>http://posulliv.github.com/2013/01/08/norevisions-field</link>
            <guid isPermaLink="true">http://posulliv.github.com/2013/01/08/norevisions-field</guid>
          </item>
        
      
    
      
        
          <item>
            <title>Field Storage Tests with Drupal 7</title>
            <description>&lt;p&gt;I had some spare time this weekend and decided to do some tests with the field storage layer. I really just wanted to re-produce the results Moshe Weitzman &lt;a href='http://cyrve.com/mongodb'&gt;published&lt;/a&gt; a while back. I also wanted to see what the best results I could get were.&lt;/p&gt;

&lt;h1 id='environment_details'&gt;Environment Details&lt;/h1&gt;

&lt;p&gt;The software and versions used for testing were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 EBS backed Large instance (8GB of memory) in the US-EAST availability zone&lt;/li&gt;

&lt;li&gt;Ubuntu 12.04 (&lt;a href='https://console.aws.amazon.com/ec2/home?region=us-east-1#launchAmi=ami-fd20ad94'&gt;ami-fd20ad94&lt;/a&gt; as listed in &lt;a href='http://cloud-images.ubuntu.com/releases/precise/release/'&gt;official ubuntu AMI&amp;#8217;s&lt;/a&gt;)&lt;/li&gt;

&lt;li&gt;MySQL 5.5.28&lt;/li&gt;

&lt;li&gt;PostgreSQL 9.2&lt;/li&gt;

&lt;li&gt;MongoDB 2.0.4&lt;/li&gt;

&lt;li&gt;Drupal 7.17&lt;/li&gt;

&lt;li&gt;Drush 5.1&lt;/li&gt;

&lt;li&gt;Migrate 2.5&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ran tests against both MySQL and PostgreSQL with default settings for both but I also ran tests where I modified the configuration of both systems to be optimized for writes.&lt;/p&gt;

&lt;p&gt;The configuration options I specified for MySQL when tuning it were:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;innodb_flush_log_at_trx_commit=0&lt;/span&gt;
&lt;span class='go'&gt;innodb_doublewrite=0&lt;/span&gt;
&lt;span class='go'&gt;log-bin=0&lt;/span&gt;
&lt;span class='go'&gt;innodb_support_xa=0&lt;/span&gt;
&lt;span class='go'&gt;innodb_buffer_pool_size=6G&lt;/span&gt;
&lt;span class='go'&gt;innodb_log_file_size=512M&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The configuration options I specified for PostgreSQL when tuning it were:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;fsync = off&lt;/span&gt;
&lt;span class='go'&gt;synchronous_commit = off&lt;/span&gt;
&lt;span class='go'&gt;wal_writer_delay = 10000ms&lt;/span&gt;
&lt;span class='go'&gt;wal_buffers = 16MB&lt;/span&gt;
&lt;span class='go'&gt;checkpoint_segments = 64&lt;/span&gt;
&lt;span class='go'&gt;shared_buffers = 6GB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id='dataset'&gt;Dataset&lt;/h1&gt;

&lt;p&gt;The dataset used for the tests comes from the &lt;a href='http://drupalcode.org/project/migrate.git/tree/refs/heads/7.x-2.x:/migrate_example_baseball'&gt;migrate_example_baseball&lt;/a&gt; module that comes as part of the migrate module. This dataset contains a box score from every Major League Baseball game from the year 2000 to the year 2009. Each year&amp;#8217;s data is contained in CSV file. Different components of the box score are saved in fields hence stressing field storage a lot.&lt;/p&gt;

&lt;h1 id='results'&gt;Results&lt;/h1&gt;

&lt;p&gt;Average throughput numbers for the various configurations I tested are shown in the table below.&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Environment&lt;/th&gt;
    &lt;th&gt;Average Throughput&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Default MySQL&lt;/td&gt;
    &lt;td&gt;1932 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Default PostgreSQL&lt;/td&gt;
    &lt;td&gt;1649 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Tuned MySQL&lt;/td&gt;
    &lt;td&gt;3024 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Tuned PostgreSQL&lt;/td&gt;
    &lt;td&gt;1772 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Default MySQL with MongoDB&lt;/td&gt;
    &lt;td&gt;4609 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Default PostgreSQL with MongoDB&lt;/td&gt;
    &lt;td&gt;4810 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Tuned MySQL with MongoDB&lt;/td&gt;
    &lt;td&gt;7671 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Tuned PostgreSQL with MongoDB&lt;/td&gt;
    &lt;td&gt;5911 nodes / minute&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;The image below shows the results graphically for different environments I tested. The Y axis is throughput (node per minute) with the X axis specifying the CSV file (corresponding to a MLB year) being imported.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/node_thruput.png' alt='Throughput numbers.' /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;h1 id='conclusion'&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Its pretty obvious from glancing at the results above that using MongoDB for field storage results in the best throughput. Tuned MySQL using MongoDB for field storage gave me the best results. This is consistent with what Moshe reported in his original article as well.&lt;/p&gt;

&lt;p&gt;What was very interesting to me was the PostgreSQL numbers. The overhead of having a table per field with the default SQL field storage seems to be very high with PostgreSQL. Its interesting to see how much better an optimized PostgreSQL does when using MongoDB for field storage.&lt;/p&gt;

&lt;p&gt;After performing these tests, one experiment I really want to try now is to create a field storage module for PostgreSQL that uses the &lt;a href='http://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.2#JSON_datatype'&gt;JSON data type&lt;/a&gt; included in the 9.2 release. Hopefully, I will get some spare time in the coming week or two to work on that.&lt;/p&gt;</description>
            <pubDate>Mon, 07 Jan 2013 00:00:00 -0800</pubDate>
            <link>http://posulliv.github.com/2013/01/07/bench-field-storage</link>
            <guid isPermaLink="true">http://posulliv.github.com/2013/01/07/bench-field-storage</guid>
          </item>
        
      
    
      
        
          <item>
            <title>Making Drupal more RESTful with Akiban</title>
            <description>&lt;p&gt;&lt;a href='http://posulliv.github.com/planet%20drupal/2012/12/14/drupal-7-install-akiban/'&gt;Last week&lt;/a&gt;, I published an article on how to install Drupal 7 with Akiban as the backend database. Today, I wanted to briefly show off our REST API using the schema that is created with a standard install of Drupal 7 core.&lt;/p&gt;

&lt;p&gt;First, I installed the &lt;a href='http://drupal.org/project/devel'&gt;devel&lt;/a&gt; module and generated some data since a bare bones install with no data would not be much fun. This server is running on a publically available EC2 instance too so if you are interested in trying these examples out yourself at home, feel free to do so! I&amp;#8217;ll leave the EC2 instance up and running for the remainder of 2012 but if anyone wants to try the examples out and the instance seems unavailable, please let me know and I&amp;#8217;ll fire it up again for you.&lt;/p&gt;

&lt;p&gt;For the first few examples, I&amp;#8217;m going to use &lt;code&gt;curl&lt;/code&gt; since its available on nearly every system (including OSX). Lets first get the version of the Akiban we are going to be interacting with:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; curl -X GET -H &lt;span class='s2'&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt; http://ec2-50-19-28-27.compute-1.amazonaws.com:8091/api/version
&lt;span class='go'&gt;[&lt;/span&gt;
&lt;span class='go'&gt;{&amp;quot;server_name&amp;quot;:&amp;quot;Akiban Server&amp;quot;,&amp;quot;server_version&amp;quot;:&amp;quot;1.4.4.2451&amp;quot;}&lt;/span&gt;
&lt;span class='go'&gt;]&lt;/span&gt;
&lt;span class='gp'&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lets continue this trend of a few simple examples to get started. I want to know the list of schemas on this server I am interacting with:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; curl -X GET -H &lt;span class='s2'&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt; http://ec2-50-19-28-27.compute-1.amazonaws.com:8091/api/information_schema.schemata
&lt;span class='go'&gt;[&lt;/span&gt;
&lt;span class='go'&gt;{&amp;quot;schema_name&amp;quot;:&amp;quot;drupal&amp;quot;,&amp;quot;schema_owner&amp;quot;:null,&amp;quot;default_character_set_name&amp;quot;:null,&amp;quot;default_collation_name&amp;quot;:null},&lt;/span&gt;
&lt;span class='go'&gt;{&amp;quot;schema_name&amp;quot;:&amp;quot;information_schema&amp;quot;,&amp;quot;schema_owner&amp;quot;:null,&amp;quot;default_character_set_name&amp;quot;:null,&amp;quot;default_collation_name&amp;quot;:null},&lt;/span&gt;
&lt;span class='go'&gt;{&amp;quot;schema_name&amp;quot;:&amp;quot;sqlj&amp;quot;,&amp;quot;schema_owner&amp;quot;:null,&amp;quot;default_character_set_name&amp;quot;:null,&amp;quot;default_collation_name&amp;quot;:null},&lt;/span&gt;
&lt;span class='go'&gt;{&amp;quot;schema_name&amp;quot;:&amp;quot;sys&amp;quot;,&amp;quot;schema_owner&amp;quot;:null,&amp;quot;default_character_set_name&amp;quot;:null,&amp;quot;default_collation_name&amp;quot;:null},&lt;/span&gt;
&lt;span class='go'&gt;{&amp;quot;schema_name&amp;quot;:&amp;quot;test&amp;quot;,&amp;quot;schema_owner&amp;quot;:null,&amp;quot;default_character_set_name&amp;quot;:null,&amp;quot;default_collation_name&amp;quot;:null}&lt;/span&gt;
&lt;span class='go'&gt;]&lt;/span&gt;
&lt;span class='gp'&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lets try a Drupal specific example next. Our REST API allows you to retrieve an entire table group in 1 request. So let&amp;#8217;s say I wanted to get all information for a certain user (I pretty-printed the JSON in the output below so if you run this you will need to format the output):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; curl -X GET -H &lt;span class='s2'&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt; http://ec2-50-19-28-27.compute-1.amazonaws.com:8091/api/drupal.users/1
&lt;span class='go'&gt;[&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;posulliv&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pass&amp;quot;: &amp;quot;$S$DPV31LZyFWJmJ.Fcj6IRyjb/RFMyQQtE87gsad7cavgnH3fw0GHA&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;posullivan@akiban.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;theme&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;signature&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;signature_format&amp;quot;: null,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355345142,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;access&amp;quot;: 1355762571,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;login&amp;quot;: 1355345211,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;timezone&amp;quot;: &amp;quot;America/New_York&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;picture&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;init&amp;quot;: &amp;quot;posullivan@akiban.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;data&amp;quot;: &amp;quot;YjowOw==&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.authmap&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.sessions&amp;quot;: [&lt;/span&gt;
&lt;span class='go'&gt;            {&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;uid&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;sid&amp;quot;: &amp;quot;jq57PowPwDK1CuKBpC56oqt_PsbwWNF4av97BuQqr6I&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;ssid&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;hostname&amp;quot;: &amp;quot;75.147.9.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;timestamp&amp;quot;: 1355762574,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;cache&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;session&amp;quot;: &amp;quot;YmF0Y2hlc3xhOjE6e2k6MTtiOjE7fWF1dGhvcml6ZV9maWxldHJhbnNmZXJfaW5mb3xhOjE6e3M6MzoiZnRwIjthOjU6e3M6NToidGl0bGUiO3M6MzoiRlRQIjtzOjU6ImNsYXNzIjtzOjE1OiJGaWxlVHJhbnNmZXJGVFAiO3M6NDoiZmlsZSI7czo3OiJmdHAuaW5jIjtzOjk6ImZpbGUgcGF0aCI7czoyMToiaW5jbHVkZXMvZmlsZXRyYW5zZmVyIjtzOjY6IndlaWdodCI7aTowO319YXV0aG9yaXplX29wZXJhdGlvbnxhOjQ6e3M6ODoiY2FsbGJhY2siO3M6Mjg6InVwZGF0ZV9hdXRob3JpemVfcnVuX2luc3RhbGwiO3M6NDoiZmlsZSI7czozNToibW9kdWxlcy91cGRhdGUvdXBkYXRlLmF1dGhvcml6ZS5pbmMiO3M6OToiYXJndW1lbnRzIjthOjM6e3M6NzoicHJvamVjdCI7czo1OiJkZXZlbCI7czoxMjoidXBkYXRlcl9uYW1lIjtzOjEzOiJNb2R1bGVVcGRhdGVyIjtzOjk6ImxvY2FsX3VybCI7czozNzoiL3RtcC91cGRhdGUtZXh0cmFjdGlvbi1kOWU4MTUzOS9kZXZlbCI7fXM6MTA6InBhZ2VfdGl0bGUiO3M6MTQ6IlVwZGF0ZSBtYW5hZ2VyIjt9bWVzc2FnZXN8YToxOntzOjU6ImVycm9yIjthOjI6e2k6MDtzOjI3NToiPGVtIGNsYXNzPSJwbGFjZWhvbGRlciI+V2FybmluZzwvZW0+OiBhcnJheV9rZXlfZXhpc3RzKCkgZXhwZWN0cyBwYXJhbWV0ZXIgMiB0byBiZSBhcnJheSwgbnVsbCBnaXZlbiBpbiA8ZW0gY2xhc3M9InBsYWNlaG9sZGVyIj50aGVtZV9pbWFnZV9mb3JtYXR0ZXIoKTwvZW0+IChsaW5lIDxlbSBjbGFzcz0icGxhY2Vob2xkZXIiPjYwNTwvZW0+IG9mIDxlbSBjbGFzcz0icGxhY2Vob2xkZXIiPi92YXIvd3d3L2RydXBhbC9tb2R1bGVzL2ltYWdlL2ltYWdlLmZpZWxkLmluYzwvZW0+KS4iO2k6MTtzOjI3NToiPGVtIGNsYXNzPSJwbGFjZWhvbGRlciI+V2FybmluZzwvZW0+OiBhcnJheV9rZXlfZXhpc3RzKCkgZXhwZWN0cyBwYXJhbWV0ZXIgMiB0byBiZSBhcnJheSwgbnVsbCBnaXZlbiBpbiA8ZW0gY2xhc3M9InBsYWNlaG9sZGVyIj50aGVtZV9pbWFnZV9mb3JtYXR0ZXIoKTwvZW0+IChsaW5lIDxlbSBjbGFzcz0icGxhY2Vob2xkZXIiPjYwNTwvZW0+IG9mIDxlbSBjbGFzcz0icGxhY2Vob2xkZXIiPi92YXIvd3d3L2RydXBhbC9tb2R1bGVzL2ltYWdlL2ltYWdlLmZpZWxkLmluYzwvZW0+KS4iO319&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;            }&lt;/span&gt;
&lt;span class='go'&gt;        ],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.shortcut_set_users&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.users_roles&amp;quot;: [&lt;/span&gt;
&lt;span class='go'&gt;            {&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;uid&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;rid&amp;quot;: 3&lt;/span&gt;
&lt;span class='go'&gt;            }&lt;/span&gt;
&lt;span class='go'&gt;        ],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.watchdog&amp;quot;: [&lt;/span&gt;
&lt;span class='go'&gt;            {&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;wid&amp;quot;: 160662,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;uid&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;type&amp;quot;: &amp;quot;php&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;message&amp;quot;: &amp;quot;JXR5cGU6ICFtZXNzYWdlIGluICVmdW5jdGlvbiAobGluZSAlbGluZSBvZiAlZmlsZSku&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;variables&amp;quot;: &amp;quot;YTo2OntzOjU6IiV0eXBlIjtzOjc6Ildhcm5pbmciO3M6ODoiIW1lc3NhZ2UiO3M6NjI6ImFycmF5X2tleV9leGlzdHMoKSBleHBlY3RzIHBhcmFtZXRlciAyIHRvIGJlIGFycmF5LCBudWxsIGdpdmVuIjtzOjk6IiVmdW5jdGlvbiI7czoyMzoidGhlbWVfaW1hZ2VfZm9ybWF0dGVyKCkiO3M6NToiJWZpbGUiO3M6NDU6Ii92YXIvd3d3L2RydXBhbC9tb2R1bGVzL2ltYWdlL2ltYWdlLmZpZWxkLmluYyI7czo1OiIlbGluZSI7aTo2MDU7czoxNDoic2V2ZXJpdHlfbGV2ZWwiO2k6NDt9&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;severity&amp;quot;: 4,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;link&amp;quot;: &amp;quot;0&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;location&amp;quot;: &amp;quot;aHR0cDovL2VjMi01MC0xOS0yOC0yNy5jb21wdXRlLTEuYW1hem9uYXdzLmNvbS9kcnVwYWwv&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;referer&amp;quot;: &amp;quot;aHR0cDovL2VjMi01MC0xOS0yOC0yNy5jb21wdXRlLTEuYW1hem9uYXdzLmNvbS9kcnVwYWwv&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;hostname&amp;quot;: &amp;quot;24.61.45.238&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;                &amp;quot;timestamp&amp;quot;: 1355406786&lt;/span&gt;
&lt;span class='go'&gt;            }&lt;/span&gt;
&lt;span class='go'&gt;        ]&lt;/span&gt;
&lt;span class='go'&gt;    }&lt;/span&gt;
&lt;span class='go'&gt;]&lt;/span&gt;
&lt;span class='gp'&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We also support multi-get so you can retrieve a number of table groups in a single REST API call. For example, lets say I want to get information on 2 users:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; curl -X GET -H &lt;span class='s2'&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://ec2-50-19-28-27.compute-1.amazonaws.com:8091/api/drupal.users/11467;10503&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;[&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 11467,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;clibriprofr&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pass&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;clibriprofr@default&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;theme&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;signature&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;signature_format&amp;quot;: null,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355360324,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;access&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;login&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;timezone&amp;quot;: null,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;picture&amp;quot;: 11463,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;init&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;data&amp;quot;: null,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.authmap&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.sessions&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.shortcut_set_users&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.users_roles&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.watchdog&amp;quot;: []&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 10503,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;uuslosuwr&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pass&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;uuslosuwr@default&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;theme&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;signature&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;signature_format&amp;quot;: null,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355360324,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;access&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;login&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;timezone&amp;quot;: null,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;picture&amp;quot;: 10499,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;init&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;data&amp;quot;: null,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.authmap&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.sessions&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.shortcut_set_users&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.users_roles&amp;quot;: [],&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;drupal.watchdog&amp;quot;: []&lt;/span&gt;
&lt;span class='go'&gt;    }&lt;/span&gt;
&lt;span class='go'&gt;]&lt;/span&gt;
&lt;span class='gp'&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our REST API also supports aribtrary SQL queries being executed and results being returned as JSON. Lets try a simple example first:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; curl -X GET -H &lt;span class='s2'&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;http://ec2-50-19-28-27.compute-1.amazonaws.com:8091/api/query?q=select%20count(*)%20from%20drupal.comment&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;[&lt;/span&gt;
&lt;span class='go'&gt;{&amp;quot;_SQL_COL_1&amp;quot;:252462}&lt;/span&gt;
&lt;span class='go'&gt;]&lt;/span&gt;
&lt;span class='gp'&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Another example of executing arbitrary SQL queries through our REST API with a more complex query follows. The query we will use for this example is:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sql'&gt;&lt;span class='k'&gt;SELECT&lt;/span&gt; &lt;span class='k'&gt;c&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt; 
&lt;span class='k'&gt;FROM&lt;/span&gt;   &lt;span class='n'&gt;drupal&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='k'&gt;comment&lt;/span&gt; &lt;span class='k'&gt;c&lt;/span&gt; 
       &lt;span class='k'&gt;INNER&lt;/span&gt; &lt;span class='k'&gt;JOIN&lt;/span&gt; &lt;span class='n'&gt;drupal&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;node&lt;/span&gt; &lt;span class='n'&gt;n&lt;/span&gt; 
               &lt;span class='k'&gt;ON&lt;/span&gt; &lt;span class='n'&gt;n&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;c&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; 
&lt;span class='k'&gt;WHERE&lt;/span&gt;  &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='k'&gt;c&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;status&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
       &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;n&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;status&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
&lt;span class='k'&gt;ORDER&lt;/span&gt;  &lt;span class='k'&gt;BY&lt;/span&gt; &lt;span class='k'&gt;c&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;created&lt;/span&gt; &lt;span class='k'&gt;DESC&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
          &lt;span class='k'&gt;c&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;cid&lt;/span&gt; &lt;span class='k'&gt;DESC&lt;/span&gt; 
&lt;span class='k'&gt;LIMIT&lt;/span&gt;  &lt;span class='mi'&gt;10&lt;/span&gt; &lt;span class='k'&gt;offset&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Running this query through our REST API and the result (again, nicely formatted) looks like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;curl -X GET -H &amp;quot;Content-Type: application/json&amp;quot; &amp;quot;http://ec2-50-19-28-27.compute-1.amazonaws.com:8091/api/query?q=SELECT%20c.*%20FROM%20drupal.comment%20c%20INNER%20JOIN%20drupal.node%20n%20ON%20n.nid%20=%20c.nid%20WHERE%20(c.status%20=%201)%20AND%20(n.status%20=%201)%20ORDER%20BY%20c.created%20DESC,%20c.cid%20DESC%20LIMIT%2010%20OFFSET%200&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;[&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304562,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93450,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;this is a test comment&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;75.147.9.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355418376,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355418376,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;01/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;posulliv&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304561,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 304558,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93451,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 3636,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;Defui Enim Gemino Luctus Occuro Paulatim&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;01.00/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;devel generate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;devel_generate@example.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304560,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93451,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 3633,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;Abdo Ea Sudo Veniam Vulputate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;03/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;devel generate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;devel_generate@example.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304559,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93451,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 3651,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;Defui Euismod Letalis Nisl Utinam Vicis&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;02/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;devel generate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;devel_generate@example.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304558,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93451,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 3657,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;Similis Te&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;01/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;devel generate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;devel_generate@example.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304557,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93448,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 3630,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;Loquor Modo Ut&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;02/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;devel generate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;devel_generate@example.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304556,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93448,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 3648,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;Abico Conventio Elit Quis&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;01/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;devel generate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;devel_generate@example.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304555,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 0,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93447,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 3646,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;Dolor Immitto Metuo Veniam&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;04/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;devel generate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;devel_generate@example.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304554,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 304553,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93447,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 3633,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;Defui Et Pertineo Premo Usitas&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;01.00.00/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;devel generate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;devel_generate@example.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    },&lt;/span&gt;
&lt;span class='go'&gt;    {&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;cid&amp;quot;: 304553,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;pid&amp;quot;: 304550,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;nid&amp;quot;: 93447,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;uid&amp;quot;: 3655,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;subject&amp;quot;: &amp;quot;Amet Gravis Inhibeo Roto Torqueo&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;hostname&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;created&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;changed&amp;quot;: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;status&amp;quot;: 1,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;thread&amp;quot;: &amp;quot;01.00/&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;name&amp;quot;: &amp;quot;devel generate&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;mail&amp;quot;: &amp;quot;devel_generate@example.com&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;homepage&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;        &amp;quot;language&amp;quot;: &amp;quot;und&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;    }&lt;/span&gt;
&lt;span class='go'&gt;]&lt;/span&gt;
&lt;span class='gp'&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, I&amp;#8217;d like to mention we have a &lt;a href='https://github.com/akiban/akiban-rest-js'&gt;client&lt;/a&gt; for &lt;code&gt;node.js&lt;/code&gt; that can be used with our REST interface. To get information on the schemas in this server and the grouping in the drupal schema, some code using this client would look as follows:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='js'&gt;&lt;span class='err'&gt;#&lt;/span&gt;&lt;span class='o'&gt;!&lt;/span&gt;&lt;span class='err'&gt;/usr/bin/env coffee&lt;/span&gt;

&lt;span class='nx'&gt;ak&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;require&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;./lib/akiban_rest.js&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nx'&gt;_&lt;/span&gt;  &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;require&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;underscore&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='nx'&gt;log&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;msg&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;========&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;msg&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;--------&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;unless&lt;/span&gt; &lt;span class='nx'&gt;arguments&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;error&lt;/span&gt;
      &lt;span class='nx'&gt;_&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;arguments&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;body&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;forEach&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;x&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;x&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;arguments&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='nx'&gt;arguments&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;error&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;--------&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='nx'&gt;x&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;new&lt;/span&gt; &lt;span class='nx'&gt;ak&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;AkibanClient&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;

&lt;span class='nx'&gt;x&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;version&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;the server version is&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='nx'&gt;x&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;schemata&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;and these are all the schemata&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='nx'&gt;x&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;groups&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;drupal&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;all groups in the drupal schema&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above can be run with the &lt;code&gt;coffee&lt;/code&gt; command like so: &lt;code&gt;coffee
drupal.coffee&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To retrieve a certain node with this client, the code would look like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='js'&gt;&lt;span class='err'&gt;#&lt;/span&gt;&lt;span class='o'&gt;!&lt;/span&gt;&lt;span class='err'&gt;/usr/bin/env coffee&lt;/span&gt;

&lt;span class='nx'&gt;ak&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;require&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;./lib/akiban_rest.js&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nx'&gt;_&lt;/span&gt;  &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;require&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;underscore&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='nx'&gt;log&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;msg&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;========&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;msg&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;--------&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;unless&lt;/span&gt; &lt;span class='nx'&gt;arguments&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;error&lt;/span&gt;
      &lt;span class='nx'&gt;_&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;arguments&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;body&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;forEach&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;x&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;x&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;arguments&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='nx'&gt;arguments&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;].&lt;/span&gt;&lt;span class='nx'&gt;error&lt;/span&gt;
    &lt;span class='nx'&gt;console&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;--------&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='nx'&gt;x&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;new&lt;/span&gt; &lt;span class='nx'&gt;ak&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;AkibanClient&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
&lt;span class='nx'&gt;x&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;drupal&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;node&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2054&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;res&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='nx'&gt;log&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;retrieving nid 2054&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)(&lt;/span&gt;&lt;span class='nx'&gt;res&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Running the above example results in output like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;$&lt;/span&gt; coffee drupal.coffee 
&lt;span class='go'&gt;========&lt;/span&gt;
&lt;span class='go'&gt;retrieving nid 2054&lt;/span&gt;
&lt;span class='go'&gt;--------&lt;/span&gt;
&lt;span class='go'&gt;{ nid: 2054,&lt;/span&gt;
&lt;span class='go'&gt;  vid: 2054,&lt;/span&gt;
&lt;span class='go'&gt;  type: &amp;#39;page&amp;#39;,&lt;/span&gt;
&lt;span class='go'&gt;  language: &amp;#39;und&amp;#39;,&lt;/span&gt;
&lt;span class='go'&gt;  title: &amp;#39;Eros Iriure Pertineo Refoveo Roto Utrum&amp;#39;,&lt;/span&gt;
&lt;span class='go'&gt;  uid: 3661,&lt;/span&gt;
&lt;span class='go'&gt;  status: 1,&lt;/span&gt;
&lt;span class='go'&gt;  created: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;  changed: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;  comment: 0,&lt;/span&gt;
&lt;span class='go'&gt;  promote: 1,&lt;/span&gt;
&lt;span class='go'&gt;  sticky: 0,&lt;/span&gt;
&lt;span class='go'&gt;  tnid: 0,&lt;/span&gt;
&lt;span class='go'&gt;  translate: 0,&lt;/span&gt;
&lt;span class='go'&gt;  &amp;#39;drupal.comment&amp;#39;: [],&lt;/span&gt;
&lt;span class='go'&gt;  &amp;#39;drupal.history&amp;#39;: [],&lt;/span&gt;
&lt;span class='go'&gt;  &amp;#39;drupal.node_access&amp;#39;: [],&lt;/span&gt;
&lt;span class='go'&gt;  &amp;#39;drupal.node_comment_statistics&amp;#39;: &lt;/span&gt;
&lt;span class='go'&gt;   [ { nid: 2054,&lt;/span&gt;
&lt;span class='go'&gt;       cid: 0,&lt;/span&gt;
&lt;span class='go'&gt;       last_comment_timestamp: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;       last_comment_name: null,&lt;/span&gt;
&lt;span class='go'&gt;       last_comment_uid: 3656,&lt;/span&gt;
&lt;span class='go'&gt;       comment_count: 0 } ],&lt;/span&gt;
&lt;span class='go'&gt;  &amp;#39;drupal.node_revision&amp;#39;: &lt;/span&gt;
&lt;span class='go'&gt;   [ { nid: 2054,&lt;/span&gt;
&lt;span class='go'&gt;       vid: 2056,&lt;/span&gt;
&lt;span class='go'&gt;       uid: 1,&lt;/span&gt;
&lt;span class='go'&gt;       title: &amp;#39;Ad Si Suscipere&amp;#39;,&lt;/span&gt;
&lt;span class='go'&gt;       log: &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='go'&gt;       timestamp: 1355369527,&lt;/span&gt;
&lt;span class='go'&gt;       status: 1,&lt;/span&gt;
&lt;span class='go'&gt;       comment: 0,&lt;/span&gt;
&lt;span class='go'&gt;       promote: 1,&lt;/span&gt;
&lt;span class='go'&gt;       sticky: 0 } ],&lt;/span&gt;
&lt;span class='go'&gt;  &amp;#39;drupal.search_node_links&amp;#39;: [] }&lt;/span&gt;
&lt;span class='go'&gt;--------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thats about it for this post showing off our REST access. As usual, comments are very much welcome and feel free to ping me on &lt;a href='https://twitter.com/intent/user?screen_name=posulliv'&gt;twitter&lt;/a&gt; if you would like to learn more about Akiban.&lt;/p&gt;</description>
            <pubDate>Mon, 17 Dec 2012 00:00:00 -0800</pubDate>
            <link>http://posulliv.github.com/2012/12/17/aiban-rest-access</link>
            <guid isPermaLink="true">http://posulliv.github.com/2012/12/17/aiban-rest-access</guid>
          </item>
        
      
    
      
        
          <item>
            <title>Installing Drupal 7 with Akiban</title>
            <description>&lt;p&gt;Dries recently published a &lt;a href='http://buytaert.net/using-the-akiban-database-with-drupal'&gt;post&lt;/a&gt; highlighting some work we&amp;#8217;ve done with a particular customer in the Acquia cloud. What I wanted to cover in this post was to how to perform an installation of Akiban and get a Drupal 7 site up and running on Akiban. This post only covers a fresh installation; later posts will cover how to do migration and augmenting an existing site instead of running it entirely on Akbian.&lt;/p&gt;

&lt;p&gt;This post is specific to Ubuntu but &lt;a href='http://akiban.com/downloads'&gt;Akiban&lt;/a&gt; runs on CentOS too (as well as OSX and Windows which we have installers for). If people would like to see information specific to those platforms, please let me know in the comments.&lt;/p&gt;

&lt;p&gt;First things first, lets install Akiban!&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo apt-get install -y python-software-properties&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 0AA4244A&lt;/span&gt;
&lt;span class='go'&gt;sudo add-apt-repository &amp;quot;deb http://software.akiban.com/apt-developer/ lucid main&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get update&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get install -y akiban-server postgresql-client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above will automatically start the Akiban server process and half of your available memory will be allocated for the JVM heap by default. If interested in modifying any configuration options, please see our &lt;a href='http://www.akiban.com/ak-docs/admin/server/server.config.html'&gt;documention&lt;/a&gt; on how to do this.&lt;/p&gt;

&lt;p&gt;Next, we&amp;#8217;ll download Drupal 7 and install Apache along with the needed PHP database drivers for Akiban.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;wget http://ftp.drupal.org/files/projects/drupal-7.17.tar.gz&lt;/span&gt;
&lt;span class='go'&gt;tar zxvf drupal-7.17.tar.gz&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get install -y apache2 php5-pgsql php5-gd libapache2-mod-php5 php-apc&lt;/span&gt;
&lt;span class='go'&gt;sudo mkdir /var/www/drupal&lt;/span&gt;
&lt;span class='go'&gt;sudo mv drupal-7.17/* drupal-7.17/.htaccess /var/www/drupal&lt;/span&gt;
&lt;span class='go'&gt;sudo cp /var/www/drupal/sites/default/default.settings.php /var/www/drupal/sites/default/settings.php&lt;/span&gt;
&lt;span class='go'&gt;sudo chown www-data:www-data /var/www/drupal/sites/default/settings.php&lt;/span&gt;
&lt;span class='go'&gt;sudo mkdir /var/www/drupal/sites/default/files&lt;/span&gt;
&lt;span class='go'&gt;sudo chown www-data:www-data /var/www/drupal/sites/default/files/&lt;/span&gt;
&lt;span class='go'&gt;sudo service apache2 restart&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The final piece of software we need is the Akiban database module for Drupal. Right now, this module is a &lt;a href='http://drupal.org/sandbox/posulliv/1835778'&gt;sandbox project on drupal.org&lt;/a&gt; so the only way to download it is to check it out with &lt;code&gt;git&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo apt-get install -y git&lt;/span&gt;
&lt;span class='go'&gt;git clone http://git.drupal.org/sandbox/posulliv/1835778.git akiban&lt;/span&gt;
&lt;span class='go'&gt;cd akiban&lt;/span&gt;
&lt;span class='go'&gt;git checkout 7.x&lt;/span&gt;
&lt;span class='go'&gt;cd ../&lt;/span&gt;
&lt;span class='go'&gt;sudo cp -R akiban /var/www/drupal/includes/database/.&lt;/span&gt;
&lt;span class='go'&gt;sudo chown -R www-data:www-data /var/www/drupal/includes/database/akiban&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice we had to switch to the &lt;code&gt;7.x&lt;/code&gt; branch. The &lt;code&gt;master&lt;/code&gt; branch in this repository is for running the module with Drupal 8.&lt;/p&gt;

&lt;p&gt;The last thing which needs to be done is apply a tiny patch to Drupal core. This patch only avoids the creation of 2 indexes in the &lt;code&gt;menu&lt;/code&gt; module. These index defitions are not compatible with Akiban with our current release. Its likely this will be resolved in a future Akiban release and so the need for this patch will be removed:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo cp akiban/core.patch /var/www/drupal&lt;/span&gt;
&lt;span class='go'&gt;cd /var/www/drupal&lt;/span&gt;
&lt;span class='go'&gt;sudo patch -p1 &amp;lt; core.patch&lt;/span&gt;
&lt;span class='go'&gt;cd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Drupal 7 can now be installed as you normally would. Just make sure to select Akiban as the database during installation!&lt;/p&gt;

&lt;p&gt;After installation completes successfully we want to group the tables and gather statistics for out cost-based optimizer. 2 SQL scripts are provided to achieve this. They can be run using &lt;code&gt;psql&lt;/code&gt; as so:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;psql -h localhost -p 15432 drupal -f akiban/grouping.sql&lt;/span&gt;
&lt;span class='go'&gt;psql -h localhost -p 15432 drupal -f akiban/gather_stats.sql&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The commands above assume &lt;code&gt;drupal&lt;/code&gt; is the name of schema in which Drupal was installed. That should obviously be changed to the name of the schema you specified during installation.&lt;/p&gt;

&lt;p&gt;Thats it! You now have a bare Drupal 7 site running on the Akiban database! I have some plans for more posts in the coming weeks. In particular, some things I plan on covering are how to migrate a Drupal site running on MySQL to Akiban and how to use Akiban as a query accelerator for a Drupal site similar to the use case in the &lt;a href='http://buytaert.net/using-the-akiban-database-with-drupal'&gt;post&lt;/a&gt; Dries wrote. I&amp;#8217;ll also show what is possible with the REST access that we enable straight to our database (hint: its on port 8091).&lt;/p&gt;

&lt;p&gt;If there is anything you would like more information on, please let me know in the comments or hit me up on &lt;a href='https://twitter.com/intent/user?screen_name=posulliv'&gt;twitter&lt;/a&gt; and I&amp;#8217;d be more than happy to dig in. We also have a &lt;a href='https://groups.google.com/a/akiban.com/d/forum/akiban-user)'&gt;public mailing list&lt;/a&gt; for the Akiban project and I&amp;#8217;d encourage anyone who&amp;#8217;s interested to subscribe to that list and let us know how we&amp;#8217;re doing!&lt;/p&gt;</description>
            <pubDate>Fri, 14 Dec 2012 00:00:00 -0800</pubDate>
            <link>http://posulliv.github.com/2012/12/14/drupal-7-install-akiban</link>
            <guid isPermaLink="true">http://posulliv.github.com/2012/12/14/drupal-7-install-akiban</guid>
          </item>
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
          <item>
            <title>Digging into Drupal's Schema</title>
            <description>&lt;p&gt;I&amp;#8217;m relatively new to Drupal internals and most of the &lt;a href='http://akiban.com/'&gt;work&lt;/a&gt; I do is on the database side. While searching for information on Drupal&amp;#8217;s schema, I found very little. During my research, I put together an ER diagram of the schema installed by Drupal 7 (D8 is very similar with only 3 extra tables at time of writing) and decided to share my work. Note that the relationships I discuss here are based on the foreign key documentation that exists in core and my understanding of what I believe other relationships could be. Corrections and comments are very much welcome.&lt;/p&gt;

&lt;h1 id='overview'&gt;Overview&lt;/h1&gt;

&lt;p&gt;I&amp;#8217;ll start off by showing my complete ER diagram below. You will see I grouped tables I found to be related in colored boxes. The image below is just meant to give a general overview of the schema. I will be diving into different parts of the schema in this post. I created this diagram using &lt;a href='http://www.mysql.com/products/workbench/'&gt;MySQL Workbench&lt;/a&gt; and the model can be downloaded from &lt;a href='http://posulliv.github.com/misc/latest_drupal_7.mwb'&gt;here&lt;/a&gt; if someone wishes to open this up in Workbench. This &lt;a href='https://gist.github.com/3231183'&gt;gist&lt;/a&gt; also shows the &lt;code&gt;ALTER TABLE&lt;/code&gt; SQL statements that would need to be issued to actually create these foreign keys in MySQL. I would not recommend doing this right now with Drupal as many things would break.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/all_drupal_7_er.png' alt='Full ER Diagram.' /&gt;
&lt;/div&gt;
&lt;p&gt;Without delving into the relationships and details of this diagram, lets first cover some basic details. A stock install of Drupal 7 results in 73 tables being created. 10 of those tables are used for caching purposes:&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Caching Table&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache&lt;/td&gt;&lt;td&gt;caches items not separated out into their own cache tables&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache_block&lt;/td&gt;&lt;td&gt;the block modules can cache already built blocks here&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache_bootstrap&lt;/td&gt;&lt;td&gt;data required during the bootstrap process can be cached in this table&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache_field&lt;/td&gt;&lt;td&gt;stores cached field values&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache_filter&lt;/td&gt;&lt;td&gt;caches already filtered pieces of text&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache_form&lt;/td&gt;&lt;td&gt;caches recently built forms and their storage data&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache_image&lt;/td&gt;&lt;td&gt;caches information about image manipulations that are in progress&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache_menu&lt;/td&gt;&lt;td&gt;caches router information as well as generated link trees&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache_page&lt;/td&gt;&lt;td&gt;caches compressed pages served to anonymous users&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;cache_path&lt;/td&gt;&lt;td&gt;caches path aliases&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;11 tables are created which do not relate to any other tables:&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Table Name&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;actions&lt;/td&gt;&lt;td&gt;stores action information&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;batch&lt;/td&gt;&lt;td&gt;stores details about batches (processes that run in multiple HTTP requests)&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;blocked_ips&lt;/td&gt;&lt;td&gt;stores a list of blocked IP addresses&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;flood&lt;/td&gt;&lt;td&gt;controls the threshold of events, such as the number of contact attempts&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;queue&lt;/td&gt;&lt;td&gt;stores items in queues&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;rdf_mapping&lt;/td&gt;&lt;td&gt;stores custom RDF mappings for user-defined content types&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;semaphore&lt;/td&gt;&lt;td&gt;stores semaphores, locks, and flags&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;sequences&lt;/td&gt;&lt;td&gt;stores IDs&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;system&lt;/td&gt;&lt;td&gt;contains a list of all modules, themes, and theme engines that are or have been installed&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;url_alias&lt;/td&gt;&lt;td&gt;contains a list of URL aliases for Drupal paths&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;variable&lt;/td&gt;&lt;td&gt;stores variable/value pairs created by Drupal core or any other module or theme&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;The 21 tables listed above are self-explanatory and I&amp;#8217;m not going to discuss them any further in this post. They also are independent in that these tables have no relationships with other tables.&lt;/p&gt;

&lt;h1 id='field_related_tables'&gt;Field Related Tables&lt;/h1&gt;

&lt;p&gt;There are 8 tables installed with core related to fields and field storage:&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Table Name&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;field_data_body&lt;/td&gt;&lt;td&gt;stores details about the body field of an entity&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;field_revision_body&lt;/td&gt;&lt;td&gt;stores information about revisions to body fields&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;field_data_comment_body&lt;/td&gt;&lt;td&gt;stores information about comments associated with an entity&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;field_revision_comment_body&lt;/td&gt;&lt;td&gt;stores information about revisions to comments&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;field_data_field_image&lt;/td&gt;&lt;td&gt;stores information about images associated with an entity&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;field_revision_field_image&lt;/td&gt;&lt;td&gt;stores information about revisions to images&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;field_data_field_tags&lt;/td&gt;&lt;td&gt;stores information about tags associated with an entity&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;field_revision_field_tags&lt;/td&gt;&lt;td&gt;stores information about revisions to taxonomy terms/tags associated with an entity&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;While I was initially tempted to have these tables related to &lt;code&gt;node&lt;/code&gt;, that would not really be correct since these tables are related to an entity. In D7, entities can be other objects besides nodes, such as users or comments. The &lt;code&gt;entity_type&lt;/code&gt; column in these tables reflects that reality. These tables can be stored in other storage systems such as &lt;a href='http://drupal.org/project/mongodb'&gt;MongoDB&lt;/a&gt; due to the &lt;a href='http://api.drupal.org/api/drupal/modules%21field%21field.attach.inc/group/field_storage/7'&gt;field storage API&lt;/a&gt; introduced in Drupal 7.&lt;/p&gt;

&lt;p&gt;There are 2 other tables related to fields: &lt;code&gt;field_config&lt;/code&gt; and &lt;code&gt;field_config_instance&lt;/code&gt;. These tables store field configuration information. I believe a row in &lt;code&gt;field_config_instance&lt;/code&gt; cannot (well at least &lt;em&gt;should&lt;/em&gt; not) exist without the correspondong &lt;code&gt;field_id&lt;/code&gt; in the &lt;code&gt;field_config&lt;/code&gt; table. Hence, the one-to-many relationship from &lt;code&gt;field_config&lt;/code&gt; to &lt;code&gt;field_config_instance&lt;/code&gt; is an identifying relationship.&lt;/p&gt;

&lt;h1 id='small_groups_of_tables'&gt;Small Groups of Tables&lt;/h1&gt;

&lt;p&gt;There are a number of groups you will notice in the full ER diagram that are made up of 2 to 3 tables. Zooming in on 4 of those groups, we can see those tables more clearly:&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/small_groups_zoom.png' alt='Zooming in on small groups.' /&gt;
&lt;/div&gt;
&lt;p&gt;One thing you will notice is that some relationships are shown with a solid line whereas others use a dotted line. MySQL Workbench represents identifying relationships with a solid line and non-identifying relationships with a dotted line. If you are unfamiliar with those terms, the standard defintions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;identifying relationship - the foreign key attribute is part of the child&amp;#8217;s primary key attribute.&lt;/li&gt;

&lt;li&gt;non-identifying relationship - the primary key attributes of the parent must not become primary key attributes of the child.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This &lt;a href='http://stackoverflow.com/questions/762937/whats-the-difference-between-identifying-and-non-identifying-relationships'&gt;stack overflow answer&lt;/a&gt; from &lt;a href='http://karwin.blogspot.com/'&gt;Bill Karwin&lt;/a&gt; contains a good discussion on these topics.&lt;/p&gt;

&lt;p&gt;Now lets discuss those groups in more detail.&lt;/p&gt;

&lt;h2 id='registry_group'&gt;Registry Group&lt;/h2&gt;

&lt;p&gt;I grouped the &lt;code&gt;registry&lt;/code&gt; and &lt;code&gt;registry_file&lt;/code&gt; tables together. These tables are used for implementing the code registry in Drupal. A one-to-many relationship exists from &lt;code&gt;registry_file&lt;/code&gt; to &lt;code&gt;registry&lt;/code&gt; and this relationship is an identifying relationship. A &lt;code&gt;filename&lt;/code&gt; should not appear in the &lt;code&gt;registry&lt;/code&gt; table that is not present in the &lt;code&gt;registry_file&lt;/code&gt; table.&lt;/p&gt;

&lt;h2 id='image_group'&gt;Image Group&lt;/h2&gt;

&lt;p&gt;I grouped the &lt;code&gt;image_styles&lt;/code&gt; and &lt;code&gt;image_effects&lt;/code&gt; tables together. These tables store configuration options for image styles and effects. A one-to-many relationship exists from &lt;code&gt;image_styles&lt;/code&gt; to &lt;code&gt;image_effects&lt;/code&gt; and this relationship is a non-identifying relationship.&lt;/p&gt;

&lt;h2 id='date_format_group'&gt;date_format Group&lt;/h2&gt;

&lt;p&gt;There are three tables about date formats in Drupal. &lt;code&gt;date_format_type&lt;/code&gt; is a lookup table that stores configured date format types. After a stock install of Drupal 7, three date format types exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;long&lt;/li&gt;

&lt;li&gt;medium&lt;/li&gt;

&lt;li&gt;short&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A one-to-many relationship exists from this lookup table to both &lt;code&gt;date_formats&lt;/code&gt; and &lt;code&gt;date_format_locale&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In practice, this would be problematic. For example, a new date format can be created by an adminstrator. In D7, this results in the &lt;code&gt;system_date_format_save&lt;/code&gt; function being called. This function will insert a row in the &lt;code&gt;date_formats&lt;/code&gt; table that will not have a corresponding type (type will be listed as custom).&lt;/p&gt;

&lt;p&gt;You will also notice the &lt;code&gt;locked&lt;/code&gt; column is redundant in the &lt;code&gt;date_formats&lt;/code&gt; table. I submitted a &lt;a href='http://drupal.org/node/1708464'&gt;patch&lt;/a&gt; to change this.&lt;/p&gt;

&lt;h2 id='file_group'&gt;File Group&lt;/h2&gt;

&lt;p&gt;I grouped the &lt;code&gt;file_managed&lt;/code&gt; and &lt;code&gt;file_usage&lt;/code&gt; tables into 1 group. These tables store information about uploaded files and information for tracking where a file is used.&lt;/p&gt;

&lt;p&gt;I believe a 1-to-1 relationship exists from &lt;code&gt;file_managed&lt;/code&gt; to &lt;code&gt;file_usage&lt;/code&gt; and that this is an identifying relationship.&lt;/p&gt;

&lt;h1 id='user_related_tables'&gt;User Related Tables&lt;/h1&gt;

&lt;p&gt;There are quite a few tables that store user related information. Below is a figure where I zoom in on those tables.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/all_user_tables.png' alt='User tables.' /&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, the tables directly associated with users are &lt;code&gt;watchdog&lt;/code&gt;, &lt;code&gt;sessions&lt;/code&gt;, and &lt;code&gt;authmap&lt;/code&gt;. These tables are in a one-to-many relationship from &lt;code&gt;users&lt;/code&gt;. The functionality these tables provide is:&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Table Name&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;authmap&lt;/td&gt;&lt;td&gt;stores distributed authentication mapping&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;sessions&lt;/td&gt;&lt;td&gt;stores information about a users session&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;watchdog&lt;/td&gt;&lt;td&gt;contains logs of all system events&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;There are then two tables that are in a many-to-many relationship with &lt;code&gt;users&lt;/code&gt; that link this table with other groups. One of these is the &lt;code&gt;users_roles&lt;/code&gt; table. This table links &lt;code&gt;users&lt;/code&gt; with &lt;code&gt;role&lt;/code&gt;. The &lt;code&gt;role&lt;/code&gt; table is then in a one-to-many relationship with the &lt;code&gt;role_permission&lt;/code&gt; table. The other many-to-many table is &lt;code&gt;shortcut_set_users&lt;/code&gt;. This table links &lt;code&gt;users&lt;/code&gt; with &lt;code&gt;shortcut_set&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The tables for the menu system are not really related to users but I placed the group close by since the &lt;code&gt;menu_links&lt;/code&gt; table maintains a one-to-many relationship with the &lt;code&gt;shortcut_set&lt;/code&gt; table. While the tables for the menu system do not appear to be related, I do believe a relationship exists there. In particular, I think that the &lt;code&gt;menu_link&lt;/code&gt; table has relationships to both the &lt;code&gt;menu_router&lt;/code&gt; and &lt;code&gt;menu_custom&lt;/code&gt; tables. The &lt;code&gt;router_path&lt;/code&gt; column in &lt;code&gt;menu_links&lt;/code&gt; could reference the &lt;code&gt;router&lt;/code&gt; column in &lt;code&gt;menu_router&lt;/code&gt; and the &lt;code&gt;menu_name&lt;/code&gt; column in &lt;code&gt;menu_links&lt;/code&gt; could reference the &lt;code&gt;menu_name&lt;/code&gt; in the &lt;code&gt;menu_custom&lt;/code&gt; table. Right now however, after a stock install of D7, a row with a menu name that is not present in &lt;code&gt;menu_custom&lt;/code&gt; will be created in &lt;code&gt;menu_links&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The menu system tables and a description of what they do is below.&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Table Name&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;menu_custom&lt;/td&gt;&lt;td&gt;holds definitions for top-level custom menus&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;menu_links&lt;/td&gt;&lt;td&gt;contains the individual links within a menu&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;menu_router&lt;/td&gt;&lt;td&gt;maps paths to various callbacks&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
&lt;h1 id='node_related_tables'&gt;Node Related Tables&lt;/h1&gt;

&lt;p&gt;Node is one of the most central concepts in Drupal so as you can imagine, many tables are related to that concept. First off, a high level overview of the tables related to the &lt;code&gt;node&lt;/code&gt; table are shown below.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/all_node_tables.png' alt='Node tables.' /&gt;
&lt;/div&gt;
&lt;p&gt;Tables that are directly related to &lt;code&gt;node&lt;/code&gt; are &lt;code&gt;node_revision&lt;/code&gt;, &lt;code&gt;node_access&lt;/code&gt;, and &lt;code&gt;node_type&lt;/code&gt;. The &lt;code&gt;node_type&lt;/code&gt; table is in many-to-many relationship with &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;block_node_type&lt;/code&gt;. &lt;code&gt;node_revision&lt;/code&gt; is in a many-to-one relationship with &lt;code&gt;node&lt;/code&gt; as is &lt;code&gt;node_access&lt;/code&gt;. The &lt;code&gt;node_access&lt;/code&gt; table has only 1 row upon initial installation and references a non-existent node. An &lt;a href='http://drupal.org/node/1703222'&gt;issue&lt;/a&gt; has been created to address this.&lt;/p&gt;

&lt;p&gt;The tables directly related to &lt;code&gt;node&lt;/code&gt; and a description of what they do is below.&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Table Name&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;node_access&lt;/td&gt;&lt;td&gt;identifies which realm/grant pairs a user must possess in order to view, update, or delete specific nodes&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;node_revision&lt;/td&gt;&lt;td&gt;stores information about each saved version of a node&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;node_type&lt;/td&gt;&lt;td&gt;stores information about all defined node types&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
&lt;h2 id='taxonomy_tables'&gt;Taxonomy Tables&lt;/h2&gt;

&lt;p&gt;Four tables in the stock schema are related to taxonomy. These tables are shown in the figure below.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/taxonomy_tables.png' alt='Taxonomy tables.' /&gt;
&lt;/div&gt;
&lt;p&gt;First of all, the &lt;code&gt;taxonomy_index&lt;/code&gt; table is in a many-to-many relationship with the &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;taxonomy_term_data&lt;/code&gt; tables. The &lt;code&gt;taxonomy_vocabulary&lt;/code&gt; table has a one-to-many relationship with the &lt;code&gt;taxonomy_term_data&lt;/code&gt; table. The &lt;code&gt;taxonomy_term_data&lt;/code&gt; table in turn has 2 1-to-many relationships with the &lt;code&gt;taxonomy_term_hierarchy&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;A description of the taxonomy tables is given below.&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Table Name&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;taxonomy_index&lt;/td&gt;&lt;td&gt;maintains de-normalized information about node/term relationships&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;taxonomy_term_data&lt;/td&gt;&lt;td&gt;stores term information&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;taxonomy_term_hierarchy&lt;/td&gt;&lt;td&gt;stores the hierarchical relationship between terms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;taxonomy_vocabulary&lt;/td&gt;&lt;td&gt;stores vocabulary information&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
&lt;h2 id='block_tables'&gt;Block Tables&lt;/h2&gt;

&lt;p&gt;The main table in this group is &lt;code&gt;block&lt;/code&gt;. It has three directly related tables in one-to-many relationships: &lt;code&gt;block_node_type&lt;/code&gt;, &lt;code&gt;block_role&lt;/code&gt;, and &lt;code&gt;block_custom&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/blocks_tables.png' alt='Blocks tables.' /&gt;
&lt;/div&gt;
&lt;p&gt;A description of the blocks tables is given below.&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Table Name&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;blocks&lt;/td&gt;&lt;td&gt;stores block settings&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;block_custom&lt;/td&gt;&lt;td&gt;stores the contents of custom-made blocks&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;block_node_type&lt;/td&gt;&lt;td&gt;stores information that sets up display criteria for blocks based on content type&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;block_role&lt;/td&gt;&lt;td&gt;stores access permissions for blocks based on user roles&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
&lt;h2 id='search_tables'&gt;Search Tables&lt;/h2&gt;

&lt;p&gt;The relationships for the search tables I am a little unsure of. I believe they are as shown in the figure below.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/search_tables.png' alt='Search tables.' /&gt;
&lt;/div&gt;
&lt;p&gt;The relationship I&amp;#8217;m most unsure of here are between &lt;code&gt;search_total&lt;/code&gt; and &lt;code&gt;search_index&lt;/code&gt;. I don&amp;#8217;t think the one-to-many relationship I have in place from &lt;code&gt;search_total&lt;/code&gt; to &lt;code&gt;search_index&lt;/code&gt; is correct.&lt;/p&gt;

&lt;p&gt;A description of the search tables is given below.&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Table Name&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;search_dataset&lt;/td&gt;&lt;td&gt;stores items that will be searched&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;search_index&lt;/td&gt;&lt;td&gt;stores the search index and associates words, items, and scores&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;search_node_links&lt;/td&gt;&lt;td&gt;stores items that link to other nodes&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;search_total&lt;/td&gt;&lt;td&gt;stores search totals for words&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
&lt;h1 id='tables_that_relate_nodes_to_users'&gt;Tables That Relate Nodes to Users&lt;/h1&gt;

&lt;p&gt;There are three tables in many-to-many relationships between &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;users&lt;/code&gt;:&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Table Name&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;comment&lt;/td&gt;&lt;td&gt;stores comments and associated data&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;history&lt;/td&gt;&lt;td&gt;stores a record of which users have read which nodes&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;node_comment_statistics&lt;/td&gt;&lt;td&gt;maintains statistics of nodes and comments posts to show &lt;b&gt;new&lt;/b&gt; and &lt;b&gt;updated&lt;/b&gt; flags&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;comment&lt;/code&gt; table could be in its own group. I decided against doing that in this ER diagram since I felt like it would have been a table by itself. Logically, I think of it as either being in the &lt;code&gt;users&lt;/code&gt; or &lt;code&gt;node&lt;/code&gt; group.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;node_comment_statistics&lt;/code&gt; does also maintain a relationship with &lt;code&gt;comment&lt;/code&gt;. This is a non-identifying relationship since a node can exist without any comments.&lt;/p&gt;

&lt;h1 id='conclusion'&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;During this work, I noticed that the column definitions for many foreign key relationships are in-correct which would result in MySQL not allowing these constraints to actually be created. I created an &lt;a href='http://drupal.org/node/1701822'&gt;issue&lt;/a&gt; and patch for this but it turns out Liam Morland is working on &lt;a href='http://drupal.org/node/911352'&gt;using foreign keys&lt;/a&gt; in core and also came across this around the same time as me.&lt;/p&gt;

&lt;p&gt;Other issues I encountered have also been logged by Liam:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;node_access&lt;/code&gt; table references a non-existent node (&lt;a href='http://drupal.org/node/1703222'&gt;relevant issue&lt;/a&gt;)&lt;/li&gt;

&lt;li&gt;a set name exists in &lt;code&gt;shortcut_set&lt;/code&gt; that does not exist in &lt;code&gt;menu_links&lt;/code&gt; (&lt;a href='http://drupal.org/node/1703208'&gt;relevant issue&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would vote for foreign keys being used in Drupal core for a number of reasons, not least of which foreign keys aid a newcomer when trying to understand the schema installed by Drupal.&lt;/p&gt;

&lt;p&gt;As I mentioned at the beginning of this post, any comments or corrections are very much welcome. I hope this information can prove useful to someone else besides me!&lt;/p&gt;</description>
            <pubDate>Thu, 02 Aug 2012 00:00:00 -0700</pubDate>
            <link>http://posulliv.github.com/2012/08/02/drupal-er-diagram</link>
            <guid isPermaLink="true">http://posulliv.github.com/2012/08/02/drupal-er-diagram</guid>
          </item>
        
      
    
      
        
      
    
      
        
          <item>
            <title>Configuring Drupal 7.x With PostgreSQL Replication</title>
            <description>&lt;p&gt;One of the new features in Drupal 7 is that it supports sending queries to a read-only slave database. Since version 9.0, PostgreSQL supports replication natively. In this post, I wanted to cover how to configure replication in PostgreSQL and have Drupal make use of a slave. I will use the master/slave terminology that is common in the MySQL world when referring to the master (primary) and slave (standby) servers in this post.&lt;/p&gt;

&lt;p&gt;First, I installed PostgreSQL 9.1 on my master server along with Drupal 7.12 The steps taken to install and configure Drupal with PostgreSQL 9.1 on my master server are outlined in this &lt;a href='https://gist.github.com/3012400'&gt;gist&lt;/a&gt;. Then I installed PostgreSQL 9.1 on another server that will serve as a slave. My initial setup on the slave was quite simple and only involved a basic install and nothing else. The following commands were all I executed on the slave server to get a base PostgreSQL install:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo apt-get install python-software-properties&lt;/span&gt;
&lt;span class='go'&gt;sudo add-apt-repository ppa:pitti/postgresql&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get update&lt;/span&gt;
&lt;span class='go'&gt;sudo apt-get install postgresql-9.1 libpq-dev postgresql-contrib-9.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once the basic Drupal install was up and running on the master and the slave server has a basic PostgreSQL install, I started on configuring replication. Replication in general is documented in depth in the online PostgreSQL &lt;a href='http://www.postgresql.org/docs/9.1/static/warm-standby.html'&gt;documentation&lt;/a&gt;. In this post, I will be configuring streaming replication which allows a slave server to service read queries.&lt;/p&gt;

&lt;p&gt;The steps that need to be performed to configure streaming replication are (I will cover how to perform each step):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a replication user for slaves to connect with&lt;/li&gt;

&lt;li&gt;enable continuous archiving on the master&lt;/li&gt;

&lt;li&gt;configure the master to allow remote connections with the replication user&lt;/li&gt;

&lt;li&gt;take a base backup to be used for setting up a slave&lt;/li&gt;

&lt;li&gt;set up a file-based log-shipping slave&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first step is to create a user for replication on the master:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo su postgres&lt;/span&gt;
&lt;span class='go'&gt;psql&lt;/span&gt;
&lt;span class='go'&gt;create role repl replication login password &amp;#39;repl&amp;#39;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next, the master needs to be have continuous archiving enabled. This is achieved by editing the &lt;code&gt;/etc/postgresql/9.1/main/postgresql.conf&lt;/code&gt; file on the master and ensuring the following parameters are set:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='nv'&gt;wal_level&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; hot_standby
&lt;span class='nv'&gt;max_wal_senders&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; 3 &lt;span class='c'&gt;# limits number of concurrent connections from standby&lt;/span&gt;
&lt;span class='nv'&gt;listen_addresses&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;0.0.0.0&amp;#39;&lt;/span&gt;
&lt;span class='nv'&gt;archive_mode&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; on
&lt;span class='nv'&gt;archive_command&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;test ! -f /mnt/postgres/archivedir/%f &amp;amp;&amp;amp; cp %p /mnt/postgres/archivedir/%f&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now to allow remote connections for the replication user, the &lt;code&gt;/etc/postgresql/9.1/main/pg_hba.conf&lt;/code&gt; file on the master server needs to have an entry like (this assumes the slave server I have configured has the IP address 10.39.111.10):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;host  replication   repl 10.39.111.10/32      md5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once the above modifications have been mode, we need to restart the PostgreSQL service:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo service postgresql restart&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The master is now configured. Next we go to the slave server to take a base backup using &lt;a href='http://www.postgresql.org/docs/9.1/static/app-pgbasebackup.html'&gt;&lt;code&gt;pg_basebackup&lt;/code&gt;&lt;/a&gt; along with configuring the slave to use this base backup for its data directory:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo service postgresql stop&lt;/span&gt;
&lt;span class='go'&gt;sudo mv /var/lib/postgresql/9.1/main/ /var/lib/postgresql/9.1/orig_main&lt;/span&gt;
&lt;span class='go'&gt;sudo su postgres&lt;/span&gt;
&lt;span class='go'&gt;pg_basebackup -D /var/lib/postgresql/9.1/main/ -P -h master_server -p 5432 -U repl -W&lt;/span&gt;
&lt;span class='go'&gt;sudo ln -s /etc/ssl/certs/ssl-cert-snakeoil.pem /var/lib/postgresql/9.1/main/server.crt&lt;/span&gt;
&lt;span class='go'&gt;sudo ln -s /etc/ssl/private/ssl-cert-snakeoil.key /var/lib/postgresql/9.1/main/server.key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;pg_basebackup&lt;/code&gt; command should result in output similar to the following:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;postgres@ip-10-39-111-9:/etc/postgresql/9.1/main$&lt;/span&gt; pg_basebackup -D /var/lib/postgresql/9.1/main/ -P -h 10.76.241.129 -p 5432 -U repl -W
&lt;span class='go'&gt;Password: &lt;/span&gt;
&lt;span class='go'&gt;WARNING:  skipping special file &amp;quot;./server.key&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;WARNING:  skipping special file &amp;quot;./server.crt&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;WARNING:  skipping special file &amp;quot;./server.key&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;WARNING:  skipping special file &amp;quot;./server.crt&amp;quot;&lt;/span&gt;
&lt;span class='go'&gt;1403786/1403786 kB (100%), 1/1 tablespace&lt;/span&gt;
&lt;span class='go'&gt;NOTICE:  pg_stop_backup complete, all required WAL segments have been archived&lt;/span&gt;
&lt;span class='gp'&gt;postgres@ip-10-39-111-9:/etc/postgresql/9.1/main$&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next, we configure the slave to be a hot standby and to allow remote client connections (since Drupal will be connecting to the slave). This is done by editing the &lt;code&gt;/etc/postgresql/9.1/main/postgresql.conf&lt;/code&gt; file on the slave to have the following entries:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='nv'&gt;hot_standby&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; on
&lt;span class='nv'&gt;listen_addresses&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;0.0.0.0&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To allow the drupal user to connect from the master server (where &lt;code&gt;apache&lt;/code&gt; is running), modify the &lt;code&gt;/etc/postgresql/9.1/main/pg_hba.conf&lt;/code&gt; file on the slave (assuming 10.76.241.129 is IP address of master):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;host  drupal drupal 10.76.241.129/32      md5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next, create a &lt;code&gt;recovery.conf&lt;/code&gt; file in the PostgreSQL data directory on the slave server:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo touch /var/lib/postgresql/9.1/main/recovery.conf&lt;/span&gt;
&lt;span class='go'&gt;sudo chown postgres:postgres /var/lib/postgresql/9.1/main/recovery.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The following should be placed in the &lt;code&gt;recovery.conf&lt;/code&gt; file (assuming 10.76.241.129 is IP address of master):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='nv'&gt;standby_mode&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;on&amp;#39;&lt;/span&gt;
&lt;span class='nv'&gt;primary_conninfo&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;host=10.76.241.129 port=5432 user=repl password=repl&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The PostgreSQL service on the slave server is now ready to be started again:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;sudo service postgresql start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If everything worked correctly, log entries indicating replication is running should be present. For example, on my slave server my log file had entries like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='gp'&gt;ubuntu@ip-10-39-111-9:/var/log/postgresql$&lt;/span&gt; sudo tail -n 5 /var/log/postgresql/postgresql-9.1-main.log 
&lt;span class='go'&gt;2012-07-07 22:06:50 UTC LOG:  streaming replication successfully connected to primary&lt;/span&gt;
&lt;span class='go'&gt;2012-07-07 22:06:50 UTC LOG:  incomplete startup packet&lt;/span&gt;
&lt;span class='go'&gt;2012-07-07 22:06:50 UTC LOG:  redo starts at 1/15000020&lt;/span&gt;
&lt;span class='go'&gt;2012-07-07 22:06:50 UTC LOG:  consistent recovery state reached at 1/16000000&lt;/span&gt;
&lt;span class='go'&gt;2012-07-07 22:06:50 UTC LOG:  database system is ready to accept read only connections&lt;/span&gt;
&lt;span class='gp'&gt;ubuntu@ip-10-39-111-9:/var/log/postgresql$&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, Drupal running on the master server is ready to be configured to use a PostgreSQL slave for read-only queries! The &lt;code&gt;settings.php&lt;/code&gt; file for the Drupal site needs to be updated to know about this slave database. My &lt;code&gt;settings.php&lt;/code&gt; file looked like (10.39.111.10 is IP address of slave server):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='php5'&gt;&lt;span class='x'&gt;$databases = array (&lt;/span&gt;
&lt;span class='x'&gt;  &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;  array (&lt;/span&gt;
&lt;span class='x'&gt;    &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;    array (&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;database&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;username&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;password&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;localhost&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;port&amp;#39; =&amp;gt; &amp;#39;5432&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;pgsql&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;prefix&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;    ),&lt;/span&gt;
&lt;span class='x'&gt;    &amp;#39;slave&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;    array (&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;database&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;username&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;password&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;10.39.111.10&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;port&amp;#39; =&amp;gt; &amp;#39;5432&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;pgsql&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;prefix&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;    ),&lt;/span&gt;
&lt;span class='x'&gt;  ),&lt;/span&gt;
&lt;span class='x'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I would suggest enabling query logging on the slave server so you can see read queries being sent to the slave. Query logging can be enabled by modifying the &lt;code&gt;/etc/postgresql/9.1/main/postgresql.conf&lt;/code&gt; file to have these entries:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='console'&gt;&lt;span class='go'&gt;logging_collector = on&lt;/span&gt;
&lt;span class='go'&gt;log_directory = &amp;#39;pg_log&amp;#39;&lt;/span&gt;
&lt;span class='go'&gt;log_statement = &amp;#39;all&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Query log files will then be generated in the &lt;code&gt;/var/lib/postgresql/9,1/main/pg_log&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;By default, very few queries from Drupal core are sent to a slave database. The search module is probably the best module to test with to see queries being sent to the slave server. The search module can be access from your drupal site by going to &lt;code&gt;http://your.ip.address/drupal/?q=search&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Try searching content for a keyword. If everything is working correctly, queries should start appearing in the query log on the slave server when issuing content searches.&lt;/p&gt;

&lt;p&gt;That&amp;#8217;s about it for this post. Once replication is configured in PostgreSQL, having Drupal send queries to the slave is pretty straightforward.&lt;/p&gt;</description>
            <pubDate>Sun, 08 Jul 2012 00:00:00 -0700</pubDate>
            <link>http://posulliv.github.com/2012/07/08/drupal-postgres-replication</link>
            <guid isPermaLink="true">http://posulliv.github.com/2012/07/08/drupal-postgres-replication</guid>
          </item>
        
      
    
      
        
          <item>
            <title>Comparing PostgreSQL 9.1 vs. MySQL 5.6 using Drupal 7.x</title>
            <description>&lt;p&gt;Its tough to come across much information about running Drupal on PostgreSQL I find beisdes the basics of installing Drupal on PostgreSQL. In particular, I&amp;#8217;m interested in comparisons of running Drupal on PostgreSQL versus MySQL. Previous posts such as this &lt;a href='http://2bits.com/articles/benchmarking-postgresql-vs-mysql-performance-using-drupal-5x.html'&gt;article&lt;/a&gt; from &lt;a href='http://2bits.com/'&gt;2bits&lt;/a&gt; compares performance of MySQL versus PostgreSQL on Drupal 5.x and seems a bit outdated. This &lt;a href='http://groups.drupal.org/node/61793'&gt;post&lt;/a&gt; from the &lt;a href='http://groups.drupal.org/high-performance'&gt;high performance drupal group&lt;/a&gt; is also pretty dated and has some information with similar comparisons.&lt;/p&gt;

&lt;p&gt;In this post, I wanted to run similar tests to what was done in the &lt;a href='http://2bits.com/articles/benchmarking-postgresql-vs-mysql-performance-using-drupal-5x.html'&gt;article&lt;/a&gt; from 2bits but on a more recent version of Drupal - 7.x. I also wanted to test out a few more complex queries that can get generated by the view module and see how they perform in MySQL versus PostgreSQL.&lt;/p&gt;

&lt;p&gt;For this post, I used the latest GA version of PostgreSQL and for kicks, I went with an aplha release of MySQL - 5.6. I would expect to see similar results for 5.5 in tests like this. I didn&amp;#8217;t use default configurations after installation since I didn&amp;#8217;t see much benefit in testing that. The configurations I used for both systems are documented below.&lt;/p&gt;

&lt;h1 id='environment_setup'&gt;Environment Setup&lt;/h1&gt;

&lt;p&gt;All results were gathered on EC2 instances. The base AMI used for these results is an official AMI of Ubuntu 10.04 provided by &lt;span&gt;Canonical&lt;/span&gt;. The particular AMI used as the base image for the results gathered in this post was &lt;a href='https://console.aws.amazon.com/ec2/home?region=us-east-1#launchAmi=ami-0baf7662'&gt;ami-0baf7662&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Images used were all launched in the US-EAST-1A availability zone and were large instance types. After launching this base image I installed MySQL 5.6 and Drupal 7.12. The steps I took to install these components along with the &lt;code&gt;my.cnf&lt;/code&gt; file I used for MySQL are outlined in this &lt;a href='https://gist.github.com/2691521'&gt;gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The PostgreSQL 9.1 setup I performed on a separate instance along with the &lt;code&gt;postgresql.conf&lt;/code&gt; settings I used are outlined in this &lt;a href='https://gist.github.com/3012400'&gt;gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;APC was installed and its default configuration was used on both servers.&lt;/p&gt;

&lt;h2 id='data_generation'&gt;Data Generation&lt;/h2&gt;

&lt;p&gt;I used &lt;a href='http://drupal.org/project/drush'&gt;drush&lt;/a&gt; and the &lt;a href='http://drupal.org/project/devel'&gt;devel&lt;/a&gt; modules to generate data. I generated the following data:&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;users&lt;/td&gt;&lt;td&gt;50000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;tags&lt;/td&gt;&lt;td&gt;1000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;vocabularies&lt;/td&gt;&lt;td&gt;5000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;menus&lt;/td&gt;&lt;td&gt;5000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;nodes&lt;/td&gt;&lt;td&gt;100000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;max comments per node&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;I generated this data in the MySQL installation first. The data was then migrated to the PostgreSQL instance using the &lt;a href='http://drupal.org/project/dbtng_migrator'&gt;dbtng_migrator&lt;/a&gt; module. This ensures the same data is used for all tests against MySQL and PostgreSQL. I covered how to perform this migration in a previous &lt;a href='http://posulliv.github.com/drupal/2012/06/26/migrate-mysql-postgres/'&gt;post&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id='pgbouncer'&gt;pgbouncer&lt;/h2&gt;

&lt;p&gt;One additional setup item I performed for PostgreSQL was to install &lt;a href='http://pgfoundry.org/projects/pgbouncer'&gt;pgbouncer&lt;/a&gt; and configure Drupal to connect through &lt;code&gt;pgbouncer&lt;/code&gt; instead of directly to PostgreSQL.&lt;/p&gt;

&lt;p&gt;Installation and configuration on Ubuntu 10.04 is straightforward. The steps to install &lt;code&gt;pgbouncer&lt;/code&gt; and the configuration I used are outlined in this &lt;a href='https://gist.github.com/3013089'&gt;gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The main reason for this change is the ApacheBench based test unfairly favors MySQL due to its process model. Each connection results in a new thread being spawned whereas with PostgreSQL, each new connection results in a new process being forked. The overhead of forking a new process is much larger than spawning a new thread. I did collect numbers for PostgreSQL without using &lt;code&gt;pgbouncer&lt;/code&gt; and I do report them in the ApacheBench test section below.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pgbouncer&lt;/code&gt; maintains a connection pool that Drupal connects so in my &lt;code&gt;settings.php&lt;/code&gt; file for my Drupal PostgreSQL instance, I modified my database settings to be:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='php'&gt;&lt;span class='x'&gt;$databases = array (&lt;/span&gt;
&lt;span class='x'&gt;  &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;  array (&lt;/span&gt;
&lt;span class='x'&gt;    &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;    array (&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;database&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;username&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;password&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;localhost&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;port&amp;#39; =&amp;gt; &amp;#39;6432&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;pgsql&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;prefix&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;    ),&lt;/span&gt;
&lt;span class='x'&gt;  ),&lt;/span&gt;
&lt;span class='x'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I performed this configuration step after I generated data in MySQL and migrated it to PostgreSQL.&lt;/p&gt;

&lt;h1 id='anonymous_users_testing_with_apachebench'&gt;Anonymous Users Testing with ApacheBench&lt;/h1&gt;

&lt;p&gt;First, loading the front page for each Drupal site with the &lt;a href='http://drupal.org/project/devel'&gt;devel&lt;/a&gt; module enabled and reporting on query execution times, the following was reported:&lt;/p&gt;
&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Database&lt;/th&gt;&lt;th&gt;Query Exec Times&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;MySQL&lt;/td&gt;&lt;td&gt;Executed 65 queries in 31.69 ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;PostgreSQL (with pgbouncer)&lt;/td&gt;
    &lt;td&gt;Executed 66 queries in 49.84 ms&lt;/td&gt;
  &lt;/tr&gt;
    &lt;td&gt;PostgreSQL&lt;/td&gt;
    &lt;td&gt;Executed 66 queries in 95 ms&lt;/td&gt;
  &lt;tr&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Straight out the gate, we can see there is not much difference here. 31 versus 50 ms is not going to be felt by many end users. If &lt;code&gt;pgbouncer&lt;/code&gt; is not used, query execution time is 3 times slower though.&lt;/p&gt;

&lt;p&gt;Next, I went to do some simple benchmarks using ApacheBench. The command used to run &lt;code&gt;ab&lt;/code&gt; was (the number of concurrent connections, X, was the only parameter varied):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;ab -c X -n 100 http://drupal.url.com/ 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;ab&lt;/code&gt; command was always run from a separate EC2 instance in the same availability zone and never on the same instance as which Drupal was running.&lt;/p&gt;

&lt;p&gt;Results obtained with default Drupal configuration (page cache disabled) but all other caching enabled are shown in the figure below. The raw numbers are presented in the table after the figure.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/first_anon_res.png' alt='First results.' /&gt;
&lt;/div&gt;&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Database&lt;/th&gt;&lt;th&gt;c = 1&lt;/th&gt;&lt;th&gt;c = 5&lt;/th&gt;&lt;th&gt;c = 10&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;MySQL&lt;/td&gt;&lt;td&gt;11.71&lt;/td&gt;&lt;td&gt;16.53&lt;/td&gt;&lt;td&gt;16.28&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;PostgreSQL (using pgbouncer)&lt;/td&gt;&lt;td&gt;8.44&lt;/td&gt;&lt;td&gt;11.03&lt;/td&gt;&lt;td&gt;11.10&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;PostgreSQL&lt;/td&gt;&lt;td&gt;4.81&lt;/td&gt;&lt;td&gt;7.32&lt;/td&gt;&lt;td&gt;7.22&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;The next test was run after all caches were cleared using &lt;code&gt;drush&lt;/code&gt;. The command issued was:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;drush cc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Option 1 was then chosen to clear all caches. This was done before each &lt;code&gt;ab&lt;/code&gt; command was run. Results are shown in the figure with raw numbers presented in the table after the figure.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/second_anon_res.png' alt='Second results.' /&gt;
&lt;/div&gt;&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Database&lt;/th&gt;&lt;th&gt;c = 1&lt;/th&gt;&lt;th&gt;c = 5&lt;/th&gt;&lt;th&gt;c = 10&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;MySQL&lt;/td&gt;&lt;td&gt;10.50&lt;/td&gt;&lt;td&gt;14.08&lt;/td&gt;&lt;td&gt;6.28&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;PostgreSQL (using pgbouncer)&lt;/td&gt;&lt;td&gt;7.92&lt;/td&gt;&lt;td&gt;9.23&lt;/td&gt;&lt;td&gt;7.32&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;PostgreSQL&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;7.04&lt;/td&gt;&lt;td&gt;6.79&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Finally, the same test was run with Drupal&amp;#8217;s page cache enabled. Results are shown in the figure below with raw numbers presented in the table after the figure.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/third_anon_res.png' alt='Third results.' /&gt;
&lt;/div&gt;&lt;table border='1'&gt;
  &lt;tr&gt;
    &lt;th&gt;Database&lt;/th&gt;&lt;th&gt;c = 1&lt;/th&gt;&lt;th&gt;c = 5&lt;/th&gt;&lt;th&gt;c = 10&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;MySQL&lt;/td&gt;&lt;td&gt;144&lt;/td&gt;&lt;td&gt;282&lt;/td&gt;&lt;td&gt;267&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;PostgreSQL (using pgbouncer)&lt;/td&gt;&lt;td&gt;120&lt;/td&gt;&lt;td&gt;205&lt;/td&gt;&lt;td&gt;202&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;PostgreSQL&lt;/td&gt;&lt;td&gt;35&lt;/td&gt;&lt;td&gt;45&lt;/td&gt;&lt;td&gt;46&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;h1 id='views_queries'&gt;Views Queries&lt;/h1&gt;

&lt;p&gt;The &lt;a href='http://drupal.org/project/views/'&gt;views&lt;/a&gt; module is known to sometimes generate queries that can cause performance problems for MySQL.&lt;/p&gt;

&lt;h2 id='image_gallery_view'&gt;Image Gallery View&lt;/h2&gt;

&lt;p&gt;The first SQL query I want to look is generated by one of the sample templates that come with the Views module. If you click &amp;#8216;Add view from template&amp;#8217; in the Views module, by default, you will only have 1 template to choose from - the Image Gallery template. After creating a view from this template and not modifying anything about that view, I see 2 problematic queries being generated.&lt;/p&gt;

&lt;p&gt;The first query is a query that counts the number of the rows in the result set for this view since this is a paginated view. The second query actually retrieves the results with a LIMIT clause and the appropriate OFFSET dependending on what page of the results the user is currently on. For this post, we&amp;#8217;ll just look at the second query that retries results. That query is:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sql'&gt;&lt;span class='k'&gt;SELECT&lt;/span&gt; &lt;span class='n'&gt;taxonomy_index&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt;      &lt;span class='k'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;taxonomy_index_tid&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
       &lt;span class='n'&gt;taxonomy_term_data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;name&lt;/span&gt; &lt;span class='k'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;taxonomy_term_data_name&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
       &lt;span class='k'&gt;Count&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;         &lt;span class='k'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;num_records&lt;/span&gt; 
&lt;span class='k'&gt;FROM&lt;/span&gt;   &lt;span class='n'&gt;node&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt; 
       &lt;span class='k'&gt;LEFT&lt;/span&gt; &lt;span class='k'&gt;JOIN&lt;/span&gt; &lt;span class='n'&gt;users&lt;/span&gt; &lt;span class='n'&gt;users_node&lt;/span&gt; 
              &lt;span class='k'&gt;ON&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;uid&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;users_node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;uid&lt;/span&gt; 
       &lt;span class='k'&gt;LEFT&lt;/span&gt; &lt;span class='k'&gt;JOIN&lt;/span&gt; &lt;span class='n'&gt;field_data_field_image&lt;/span&gt; &lt;span class='n'&gt;field_data_field_image&lt;/span&gt; 
              &lt;span class='k'&gt;ON&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;field_data_field_image&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;entity_id&lt;/span&gt; 
                 &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;field_data_field_image&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;entity_type&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;node&amp;#39;&lt;/span&gt; 
                       &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='n'&gt;field_data_field_image&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;deleted&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;0&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
       &lt;span class='k'&gt;LEFT&lt;/span&gt; &lt;span class='k'&gt;JOIN&lt;/span&gt; &lt;span class='n'&gt;taxonomy_index&lt;/span&gt; &lt;span class='n'&gt;taxonomy_index&lt;/span&gt; 
              &lt;span class='k'&gt;ON&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;taxonomy_index&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; 
       &lt;span class='k'&gt;LEFT&lt;/span&gt; &lt;span class='k'&gt;JOIN&lt;/span&gt; &lt;span class='n'&gt;taxonomy_term_data&lt;/span&gt; &lt;span class='n'&gt;taxonomy_term_data&lt;/span&gt; 
              &lt;span class='k'&gt;ON&lt;/span&gt; &lt;span class='n'&gt;taxonomy_index&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;taxonomy_term_data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; 
&lt;span class='k'&gt;WHERE&lt;/span&gt;  &lt;span class='p'&gt;((&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;field_data_field_image&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;field_image_fid&lt;/span&gt; &lt;span class='k'&gt;IS&lt;/span&gt; &lt;span class='k'&gt;NOT&lt;/span&gt; &lt;span class='k'&gt;NULL&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
          &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;status&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;))&lt;/span&gt; 
&lt;span class='k'&gt;GROUP&lt;/span&gt;  &lt;span class='k'&gt;BY&lt;/span&gt; &lt;span class='n'&gt;taxonomy_term_data_name&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
          &lt;span class='n'&gt;taxonomy_index_tid&lt;/span&gt; 
&lt;span class='k'&gt;ORDER&lt;/span&gt;  &lt;span class='k'&gt;BY&lt;/span&gt; &lt;span class='n'&gt;num_records&lt;/span&gt; &lt;span class='k'&gt;ASC&lt;/span&gt; 
&lt;span class='k'&gt;LIMIT&lt;/span&gt;  &lt;span class='mi'&gt;24&lt;/span&gt; &lt;span class='k'&gt;offset&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The response time of the query in MySQL versus PostgreSQL is shown in the figure below.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/first_query_response_time.png' alt='First query response time results.' /&gt;
&lt;/div&gt;
&lt;p&gt;As seen in the image above, PostgreSQL can execute the query in question in 300ms or less whereas MySQL consistently takes 2800 ms to execute the query.&lt;/p&gt;

&lt;p&gt;The MySQL execution plan looks like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: field_data_field_image
         &lt;span class='nb'&gt;type&lt;/span&gt;: ref
possible_keys: PRIMARY,entity_type,deleted,entity_id,field_image_fid
          key: PRIMARY
      key_len: 386
          ref: const
         rows: 19165
        Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: node
         &lt;span class='nb'&gt;type&lt;/span&gt;: eq_ref
possible_keys: PRIMARY,node_status_type
          key: PRIMARY
      key_len: 4
          ref: drupal.field_data_field_image.entity_id
         rows: 1
        Extra: Using where
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: users_node
         &lt;span class='nb'&gt;type&lt;/span&gt;: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: drupal.node.uid
         rows: 1
        Extra: Using where; Using index
*************************** 4. row ***************************
           id: 1
  select_type: SIMPLE
        table: taxonomy_index
         &lt;span class='nb'&gt;type&lt;/span&gt;: ref
possible_keys: nid
          key: nid
      key_len: 4
          ref: drupal.field_data_field_image.entity_id
         rows: 1
        Extra: NULL
*************************** 5. row ***************************
           id: 1
  select_type: SIMPLE
        table: taxonomy_term_data
         &lt;span class='nb'&gt;type&lt;/span&gt;: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: drupal.taxonomy_index.tid
         rows: 1
        Extra: NULL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;MySQL starts from the &lt;code&gt;field_date_field_image&lt;/code&gt; table and since there is no selective predicates in the query, chooses to scan the table using the &lt;code&gt;PRIMARY&lt;/code&gt; key of the table. It then filters the rows scanned using the &lt;code&gt;field_image_fid IS NOT NULL&lt;/code&gt; predicate. Since MySQL only has 1 join algorithm, nested loops, it is used to perform the remainder of the joins. A temporary table is created in memory to store the results of these joins. This is then sorted and the result set limited to the 24 requested.&lt;/p&gt;

&lt;p&gt;The PostgreSQL execution plan looks drastically different.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt; Limit  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;11712.83..11712.89 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;24 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;20&lt;span class='o'&gt;)&lt;/span&gt;
   -&amp;gt;  Sort  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;11712.83..11829.24 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;46564 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;20&lt;span class='o'&gt;)&lt;/span&gt;
         Sort Key: &lt;span class='o'&gt;(&lt;/span&gt;count&lt;span class='o'&gt;(&lt;/span&gt;node.nid&lt;span class='o'&gt;))&lt;/span&gt;
         -&amp;gt;  HashAggregate  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;9946.90..10412.54 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;46564 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;20&lt;span class='o'&gt;)&lt;/span&gt;
               -&amp;gt;  Hash Left Join  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;6174.69..9597.67 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;46564 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;20&lt;span class='o'&gt;)&lt;/span&gt;
                     Hash Cond: &lt;span class='o'&gt;(&lt;/span&gt;taxonomy_index.tid &lt;span class='o'&gt;=&lt;/span&gt; taxonomy_term_data.tid&lt;span class='o'&gt;)&lt;/span&gt;
                     -&amp;gt;  Hash Right Join  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;6140.19..8922.92 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;46564 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;12&lt;span class='o'&gt;)&lt;/span&gt;
                           Hash Cond: &lt;span class='o'&gt;(&lt;/span&gt;taxonomy_index.nid &lt;span class='o'&gt;=&lt;/span&gt; node.nid&lt;span class='o'&gt;)&lt;/span&gt;
                           -&amp;gt;  Seq Scan on taxonomy_index  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..1510.18 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;92218 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;16&lt;span class='o'&gt;)&lt;/span&gt;
                           -&amp;gt;  Hash  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;5657.14..5657.14 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;38644 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;4&lt;span class='o'&gt;)&lt;/span&gt;
                                 -&amp;gt;  Hash Join  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;2030.71..5657.14 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;38644 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;4&lt;span class='o'&gt;)&lt;/span&gt;
                                       Hash Cond: &lt;span class='o'&gt;(&lt;/span&gt;node.nid &lt;span class='o'&gt;=&lt;/span&gt; field_data_field_image.entity_id&lt;span class='o'&gt;)&lt;/span&gt;
                                       -&amp;gt;  Seq Scan on node  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..2187.66 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;76533 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;8&lt;span class='o'&gt;)&lt;/span&gt;
                                             Filter: &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;status&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; 1&lt;span class='o'&gt;)&lt;/span&gt;
                                       -&amp;gt;  Hash  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;1547.66..1547.66 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;38644 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;8&lt;span class='o'&gt;)&lt;/span&gt;
                                             -&amp;gt;  Seq Scan on field_data_field_image  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..1547.66 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;38644 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;8&lt;span class='o'&gt;)&lt;/span&gt;
                                                   Filter: &lt;span class='o'&gt;((&lt;/span&gt;field_image_fid IS NOT NULL&lt;span class='o'&gt;)&lt;/span&gt; AND &lt;span class='o'&gt;((&lt;/span&gt;entity_type&lt;span class='o'&gt;)&lt;/span&gt;::text &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;node&amp;#39;&lt;/span&gt;::text&lt;span class='o'&gt;)&lt;/span&gt; AND &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;deleted&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; 0::smallint&lt;span class='o'&gt;))&lt;/span&gt;
                     -&amp;gt;  Hash  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;22.00..22.00 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;1000 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;12&lt;span class='o'&gt;)&lt;/span&gt;
                           -&amp;gt;  Seq Scan on taxonomy_term_data  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..22.00 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;1000 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;12&lt;span class='o'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PostgreSQL has a number of other join algorithms available for use. In particular, for this query, the optimizer has decided that a hash join is the optimal choice.&lt;/p&gt;

&lt;p&gt;PostgreSQL starts by scanning the tiny (1000 rows) &lt;code&gt;taxonomy_term_data&lt;/code&gt; table and constructing an in-memory hash table (the build phase in a hash join). It then probes this hash table for possible matches of &lt;code&gt;taxonomy_index.tid = taxonomy_term_data.tid&lt;/code&gt; for each row that results from a hash join of &lt;code&gt;taxonomy_index&lt;/code&gt; and &lt;code&gt;node&lt;/code&gt;. This hash join was a result of the &lt;code&gt;field_data_field_image&lt;/code&gt; and &lt;code&gt;node&lt;/code&gt; table being join with the &lt;code&gt;field_data_field_image&lt;/code&gt; being used to build a hash table and a sequential scan of &lt;code&gt;node&lt;/code&gt; being used to probe that hash table. Aggregation is then performed and the result set is then sorted by the aggregated value (in this case a count of node ids). Finally, the result set is limited to 24.&lt;/p&gt;

&lt;p&gt;One neat thing about PostgreSQL is planner nodes can be disabled. So to make PostgreSQL execute the query in a similar manner to MySQL, I did:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='nv'&gt;drupal&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&amp;gt; &lt;span class='nb'&gt;set &lt;/span&gt;&lt;span class='nv'&gt;enable_hashjoin&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;off;
SET
&lt;span class='nv'&gt;drupal&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&amp;gt; &lt;span class='nb'&gt;set &lt;/span&gt;&lt;span class='nv'&gt;enable_hashagg&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;off;
SET
&lt;span class='nv'&gt;drupal&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&amp;gt; &lt;span class='nb'&gt;set &lt;/span&gt;&lt;span class='nv'&gt;enable_mergejoin&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;off;
SET
&lt;span class='nv'&gt;drupal&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the execution plan PostgreSQL chose then was:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt; Limit  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;52438.04..52438.10 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;24 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;20&lt;span class='o'&gt;)&lt;/span&gt;
   -&amp;gt;  Sort  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;52438.04..52552.82 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;45913 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;20&lt;span class='o'&gt;)&lt;/span&gt;
         Sort Key: &lt;span class='o'&gt;(&lt;/span&gt;count&lt;span class='o'&gt;(&lt;/span&gt;node.nid&lt;span class='o'&gt;))&lt;/span&gt;
         -&amp;gt;  GroupAggregate  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;50237.67..51155.93 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;45913 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;20&lt;span class='o'&gt;)&lt;/span&gt;
               -&amp;gt;  Sort  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;50237.67..50352.45 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;45913 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;20&lt;span class='o'&gt;)&lt;/span&gt;
                     Sort Key: taxonomy_term_data.name, taxonomy_index.tid
                     -&amp;gt;  Nested Loop Left Join  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..46682.48 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;45913 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;20&lt;span class='o'&gt;)&lt;/span&gt;
                           -&amp;gt;  Nested Loop Left Join  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..33783.81 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;45913 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;12&lt;span class='o'&gt;)&lt;/span&gt;
                                 -&amp;gt;  Nested Loop  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..18575.38 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;38644 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;4&lt;span class='o'&gt;)&lt;/span&gt;
                                       -&amp;gt;  Seq Scan on field_data_field_image  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..1547.66 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;38644 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;8&lt;span class='o'&gt;)&lt;/span&gt;
                                             Filter: &lt;span class='o'&gt;((&lt;/span&gt;field_image_fid IS NOT NULL&lt;span class='o'&gt;)&lt;/span&gt; AND &lt;span class='o'&gt;((&lt;/span&gt;entity_type&lt;span class='o'&gt;)&lt;/span&gt;::text &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;node&amp;#39;&lt;/span&gt;::text&lt;span class='o'&gt;)&lt;/span&gt; AND &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;deleted&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; 0::smallint&lt;span class='o'&gt;))&lt;/span&gt;
                                       -&amp;gt;  Index Scan using node_pkey on node  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..0.43 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;1 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;8&lt;span class='o'&gt;)&lt;/span&gt;
                                             Index Cond: &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;nid&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; field_data_field_image.entity_id&lt;span class='o'&gt;)&lt;/span&gt;
                                             Filter: &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;status&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; 1&lt;span class='o'&gt;)&lt;/span&gt;
                                 -&amp;gt;  Index Scan using taxonomy_index_nid_idx on taxonomy_index  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..0.36 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;3 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;16&lt;span class='o'&gt;)&lt;/span&gt;
                                       Index Cond: &lt;span class='o'&gt;(&lt;/span&gt;node.nid &lt;span class='o'&gt;=&lt;/span&gt; nid&lt;span class='o'&gt;)&lt;/span&gt;
                           -&amp;gt;  Index Scan using taxonomy_term_data_pkey on taxonomy_term_data  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..0.27 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;1 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;12&lt;span class='o'&gt;)&lt;/span&gt;
                                 Index Cond: &lt;span class='o'&gt;(&lt;/span&gt;taxonomy_index.tid &lt;span class='o'&gt;=&lt;/span&gt; tid&lt;span class='o'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above plan takes 2 seconds to execute against PostgreSQL. You can see it is very similar to the MySQL plan. It starts with the &lt;code&gt;field_data_field_image&lt;/code&gt; table and performs nested loop joins to join the remainder of the tables. In this case, a sort must be performed before the aggregation that is expensive to perform. Using the HashAggregate operator in PostgreSQL would greatly reduce that cost.&lt;/p&gt;

&lt;p&gt;So you can see out of the box, PostgreSQL performs much better on this query.&lt;/p&gt;

&lt;h2 id='simple_view'&gt;Simple View&lt;/h2&gt;

&lt;p&gt;I created a simple view that filters and sorts on content criteria. A screenshot of my view construction page can be seen &lt;a href='/images/view_screen_shot.png'&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The resulting SQL query that gets executed by this view is:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sql'&gt;&lt;span class='k'&gt;SELECT&lt;/span&gt; &lt;span class='k'&gt;DISTINCT&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;title&lt;/span&gt;                            &lt;span class='k'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;node_title&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
                &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt;                              &lt;span class='k'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;nid&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
                &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;comment_count&lt;/span&gt; &lt;span class='k'&gt;AS&lt;/span&gt; 
                &lt;span class='n'&gt;node_comment_statistics_comment_count&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
                &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;created&lt;/span&gt;                          &lt;span class='k'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;node_created&lt;/span&gt; 
&lt;span class='k'&gt;FROM&lt;/span&gt;   &lt;span class='n'&gt;node&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt; 
       &lt;span class='k'&gt;INNER&lt;/span&gt; &lt;span class='k'&gt;JOIN&lt;/span&gt; &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt; &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt; 
         &lt;span class='k'&gt;ON&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; 
&lt;span class='k'&gt;WHERE&lt;/span&gt;  &lt;span class='p'&gt;((&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;status&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
          &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='k'&gt;comment&lt;/span&gt; &lt;span class='k'&gt;IN&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;2&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
          &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;111&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
          &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;comment_count&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;2&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='k'&gt;ORDER&lt;/span&gt;  &lt;span class='k'&gt;BY&lt;/span&gt; &lt;span class='n'&gt;node_created&lt;/span&gt; &lt;span class='k'&gt;ASC&lt;/span&gt; 
&lt;span class='k'&gt;LIMIT&lt;/span&gt;  &lt;span class='mi'&gt;50&lt;/span&gt; &lt;span class='k'&gt;offset&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The response time of the query in MySQL versus PostgreSQL is shown in the figure below.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/second_query_response_time.png' alt='Second query response time results.' /&gt;
&lt;/div&gt;
&lt;p&gt;As seen in the image above, PostgreSQL can execute the query in question in 200ms or less whereas MySQL can take up to 1000 ms to execute the query.&lt;/p&gt;

&lt;p&gt;The MySQL execution plan looks like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: node
         &lt;span class='nb'&gt;type&lt;/span&gt;: index
possible_keys: PRIMARY,node_status_type
          key: node_created
      key_len: 4
          ref: NULL
         rows: 100
        Extra: Using where; Using temporary
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: node_comment_statistics
         &lt;span class='nb'&gt;type&lt;/span&gt;: eq_ref
possible_keys: PRIMARY,comment_count
          key: PRIMARY
      key_len: 4
          ref: drupal.node.nid
         rows: 1
        Extra: Using where
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;MySQL chooses to start from the &lt;code&gt;node&lt;/code&gt; table and scans an index on the created column. A temporary table is then created in memory to store the results of this index scan. The items stored in the temporary table are then processed to eliminate duplicates (for the &lt;code&gt;DISTINCT&lt;/code&gt;). For each distinct row in the temporary table, MySQL then performs a join to the &lt;code&gt;node_comment_statistics&lt;/code&gt; table by performing an index lookup using its primary key.&lt;/p&gt;

&lt;p&gt;The PostgreSQL execution plan for this query looks like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt; Limit  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;6207.15..6207.27 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;50 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;42&lt;span class='o'&gt;)&lt;/span&gt;
   -&amp;gt;  Sort  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;6207.15..6250.75 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;17441 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;42&lt;span class='o'&gt;)&lt;/span&gt;
         Sort Key: node.created
         -&amp;gt;  HashAggregate  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;5453.36..5627.77 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;17441 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;42&lt;span class='o'&gt;)&lt;/span&gt;
               -&amp;gt;  Hash Join  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;1985.31..5278.95 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;17441 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;42&lt;span class='o'&gt;)&lt;/span&gt;
                     Hash Cond: &lt;span class='o'&gt;(&lt;/span&gt;node.nid &lt;span class='o'&gt;=&lt;/span&gt; node_comment_statistics.nid&lt;span class='o'&gt;)&lt;/span&gt;
                     -&amp;gt;  Seq Scan on node  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..2589.32 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;38539 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;34&lt;span class='o'&gt;)&lt;/span&gt;
                           Filter: &lt;span class='o'&gt;((&lt;/span&gt;nid &amp;gt;&lt;span class='o'&gt;=&lt;/span&gt; 111&lt;span class='o'&gt;)&lt;/span&gt; AND &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;status&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; 1&lt;span class='o'&gt;)&lt;/span&gt; AND &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;comment&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; 2&lt;span class='o'&gt;))&lt;/span&gt;
                     -&amp;gt;  Hash  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;1546.22..1546.22 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;35127 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;16&lt;span class='o'&gt;)&lt;/span&gt;
                           -&amp;gt;  Seq Scan on node_comment_statistics  &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;cost&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;0.00..1546.22 &lt;span class='nv'&gt;rows&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;35127 &lt;span class='nv'&gt;width&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;16&lt;span class='o'&gt;)&lt;/span&gt;
                                 Filter: &lt;span class='o'&gt;(&lt;/span&gt;comment_count &amp;gt;&lt;span class='o'&gt;=&lt;/span&gt; 2::bigint&lt;span class='o'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PostgreSQL chooses to start by scanning the &lt;code&gt;node_comment_statistics&lt;/code&gt; table and building an in-memory hash table. This hash table is then probed for possible mathces of &lt;code&gt;node.nid = node_comment_statistics.nid&lt;/code&gt; for each row that results from a sequential scan of the &lt;code&gt;node&lt;/code&gt; table. The result of this hash join is then aggregated (for the &lt;code&gt;DISTINCT&lt;/code&gt;) before being sorted and limited to 50 rows.&lt;/p&gt;

&lt;p&gt;Its worth noting that with out of the box settings, the above query would do a disk based sort (sort method is viewable using &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; in PostgreSQL). When doing a disk based sort, the query takes about 450 ms to execute. I was running all my tests with &lt;code&gt;work_mem&lt;/code&gt; set to 4MB though which results in a top-N heapsort being used.&lt;/p&gt;

&lt;h1 id='conclusion'&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;In my opinion, the only issue with using PostgreSQL as your Drupal database is that some contributed modules will not work out of the box with that configuration.&lt;/p&gt;

&lt;p&gt;Certainly, from a performance point of view, I see no issues with using PostgreSQL with Drupal. In fact, for Drupal sites using the Views module (probably the majority), I would say PostgreSQL is probably even a better option than MySQL due to its more advanced optimizer and execution engine. This does assume &lt;code&gt;pgbouncer&lt;/code&gt; is being used and Drupal is not connecting directly to PostgreSQL. Users who do not use &lt;code&gt;pgbouncer&lt;/code&gt; and perform simple benchmarks like the ones I did with &lt;code&gt;ab&lt;/code&gt; are likely to see poor performance against PostgreSQL.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m working a lot with Drupal on PostgreSQL these days. I&amp;#8217;ll be sure to share any interesting experiences I have here.&lt;/p&gt;</description>
            <pubDate>Fri, 29 Jun 2012 00:00:00 -0700</pubDate>
            <link>http://posulliv.github.com/2012/06/29/mysql-postgres-bench</link>
            <guid isPermaLink="true">http://posulliv.github.com/2012/06/29/mysql-postgres-bench</guid>
          </item>
        
      
    
      
        
          <item>
            <title>Migrating Drupal 7 Site from MySQL to PostgreSQL on Ubuntu 10.04</title>
            <description>&lt;p&gt;I recently needed to migrate a Drupal 7 site running on a MySQL 5.5 database to a PostgreSQL 9.1 database. This brief post describes the steps I took to achieve this. The steps outlined here were only tested on Ubuntu 10.04&lt;/p&gt;

&lt;p&gt;First, I installed a fresh copy of PostgreSQL 9.1.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;sudo apt-get install python-software-properties
sudo add-apt-repository ppa:pitti/postgresql
sudo apt-get update
sudo apt-get install postgresql-9.1 libpq-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After the installation is complete, a schema and user account is created for Drupal.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;sudo su postgres
createuser -D -A -P drupal
createdb --encoding&lt;span class='o'&gt;=&lt;/span&gt;UTF8 -O drupal drupal
&lt;span class='nb'&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above creates a user account named drupal (you will be prompted for a password for the user account when running the command) and a schema named drupal.&lt;/p&gt;

&lt;p&gt;Next, PostgreSQL needs to be configured to allow connections from Apache for Drupal. This is done by modifying the &lt;code&gt;/etc/postgresql/9.1/main/pg_hba.conf&lt;/code&gt; file. The following line needs to be commented out or deleted:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='nb'&gt;local   &lt;/span&gt;all             all                                     peer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The line to added in this file is:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;host    drupal          drupal          127.0.0.1/32            password
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After this file is modified, PostgreSQL needs to be restarted.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;sudo service postgresql restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the migration, we are going to assume &lt;a href='http://drupal.org/project/drush'&gt;drush&lt;/a&gt; is installed on the server we will be performing the migration. We are also going to assume MySQL and PostgreSQL are running on the same server although this is certainly not a requirement for these instructions.&lt;/p&gt;

&lt;p&gt;The module that performs the real work of the migration is the &lt;a href='http://drupal.org/project/dbtng_migrator'&gt;dbtng_migrator&lt;/a&gt; module. This module is installed in the same manner as any other Drupal module. After the module is installed, the &lt;code&gt;settings.php&lt;/code&gt; file for your drupal installation then needs to be modified to point to your source and destination database. In my case, I updated my &lt;code&gt;settings.php&lt;/code&gt; file to look like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='php'&gt;&lt;span class='x'&gt;$databases = array (&lt;/span&gt;
&lt;span class='x'&gt;  &amp;#39;default&amp;#39; =&amp;gt; array (&lt;/span&gt;
&lt;span class='x'&gt;    &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;      array (&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;database&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;username&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;password&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;localhost&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;port&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;mysql&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;prefix&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      ),&lt;/span&gt;
&lt;span class='x'&gt;  ),&lt;/span&gt;
&lt;span class='x'&gt;  &amp;#39;dest&amp;#39; =&amp;gt; array (&lt;/span&gt;
&lt;span class='x'&gt;    &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;      array (&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;database&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;username&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;password&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;localhost&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;port&amp;#39; =&amp;gt;&amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;pgsql&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;        &amp;#39;prefix&amp;#39; =&amp;gt;&amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      ),&lt;/span&gt;
&lt;span class='x'&gt;    ),&lt;/span&gt;
&lt;span class='x'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see in my case, the default schema that I am currently running on is a MySQL database and I am planning on migrating to a PostgreSQL database running on the same machine.&lt;/p&gt;

&lt;p&gt;Now, to perform the migration from the command line using &lt;code&gt;drush&lt;/code&gt;, its as simple as:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;drush cache-clear drush
drush dbtng-replicate default dest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When the migration finishes, output similar to the following will be seen (this is just a small portion of the output):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;drush dbtng-replicate default dest
...
cache_update successfully migrated.                    &lt;span class='o'&gt;[&lt;/span&gt;status&lt;span class='o'&gt;]&lt;/span&gt;
authmap successfully migrated.                         &lt;span class='o'&gt;[&lt;/span&gt;status&lt;span class='o'&gt;]&lt;/span&gt;
role_permission successfully migrated.                 &lt;span class='o'&gt;[&lt;/span&gt;status&lt;span class='o'&gt;]&lt;/span&gt;
role successfully migrated.                            &lt;span class='o'&gt;[&lt;/span&gt;status&lt;span class='o'&gt;]&lt;/span&gt;
users successfully migrated.                           &lt;span class='o'&gt;[&lt;/span&gt;status&lt;span class='o'&gt;]&lt;/span&gt;
users_roles successfully migrated.                     &lt;span class='o'&gt;[&lt;/span&gt;status&lt;span class='o'&gt;]&lt;/span&gt;
&lt;span class='err'&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, after the database migration is successfully completed, the &lt;code&gt;settings.php&lt;/code&gt; file needs to be updated to point to the new database. In my case, the database settings after my migration looked like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='php'&gt;&lt;span class='x'&gt;$databases = array (&lt;/span&gt;
&lt;span class='x'&gt;  &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;  array (&lt;/span&gt;
&lt;span class='x'&gt;    &amp;#39;default&amp;#39; =&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;    array (&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;database&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;username&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;password&amp;#39; =&amp;gt; &amp;#39;drupal&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;localhost&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;port&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;pgsql&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;      &amp;#39;prefix&amp;#39; =&amp;gt; &amp;#39;&amp;#39;,&lt;/span&gt;
&lt;span class='x'&gt;    ),&lt;/span&gt;
&lt;span class='x'&gt;  ),&lt;/span&gt;
&lt;span class='x'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That was it for my migration. Granted, I had a small drupal site to migrate and the only additional modules I had installed were the views and devel modules so I did not need to worry about contributed modules working with the PostgreSQL database. Next step would be to be configure PostgreSQL in a more optimal which I did not go in to here.&lt;/p&gt;</description>
            <pubDate>Tue, 26 Jun 2012 00:00:00 -0700</pubDate>
            <link>http://posulliv.github.com/2012/06/26/migrate-mysql-postgres</link>
            <guid isPermaLink="true">http://posulliv.github.com/2012/06/26/migrate-mysql-postgres</guid>
          </item>
        
      
    
      
        
      
    
      
        
          <item>
            <title>Using Akiban Server with Drupal 6</title>
            <description>&lt;p&gt;In my &lt;a href='http://posulliv.github.com/2012/05/14/drupal-akiban.html'&gt;previous post&lt;/a&gt;, I mentioned I&amp;#8217;m working on a database driver for Drupal 7 for the Akiban Server. However, we have some clients who use Drupal 6 so I wanted to talk about how we work with those clients in this post.&lt;/p&gt;

&lt;p&gt;Drupal 6 does not have a database abstraction layer so it is not as easy to integrate Akiban in this case. With Drupal 6, we do not attempt to run all of Drupal on Akiban. What we have done with our existing customers who use Drupal 6 is to send certain problem queries to Akiban (running in our MySQL replication configuration) and everything else to MySQL. The Akiban Server speaks the PostgreSQL protocol as I discussed &lt;a href='http://www.akiban.com/blog/2011/08/29/typical-akiban-deployment'&gt;before&lt;/a&gt;. Hence, for Drupal 6, we use the PostgreSQL database driver to talk to Akiban.&lt;/p&gt;

&lt;p&gt;Since Drupal 6 does not support speaking to multiple different database backends at the same time, we apply a &lt;a href='https://gist.github.com/2710166'&gt;patch&lt;/a&gt; to get started. Basically, this patch allows connections to be open to both an existing MySQL server and the Akiban Server at the same time. It could be used to do the same with a PostgreSQL database as well.&lt;/p&gt;

&lt;p&gt;Once that patch is applied, to send a query to Akiban, the active database connection is set to Akiban and a query is fired off for the Akiban Server to execute. The following code snippet shows an example.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='php'&gt;&lt;span class='x'&gt;if ($this-&amp;gt;use_akiban) {&lt;/span&gt;
&lt;span class='x'&gt;   db_set_active(&amp;#39;akiban&amp;#39;);&lt;/span&gt;
&lt;span class='x'&gt;   $result = db_query_range($query, &lt;/span&gt;
&lt;span class='x'&gt;                            $args, &lt;/span&gt;
&lt;span class='x'&gt;                            $offset, &lt;/span&gt;
&lt;span class='x'&gt;                            $this-&amp;gt;pager[&amp;#39;items_per_page&amp;#39;]);&lt;/span&gt;
&lt;span class='x'&gt;}&lt;/span&gt;
&lt;span class='x'&gt;db_set_active(&amp;#39;default&amp;#39;)&lt;/span&gt;
&lt;span class='x'&gt;if (! $result) { /* if Akiban failed go to regular MySQL */&lt;/span&gt;
&lt;span class='x'&gt;   $result = db_query_range($query, &lt;/span&gt;
&lt;span class='x'&gt;                            $args, &lt;/span&gt;
&lt;span class='x'&gt;                            $offset, &lt;/span&gt;
&lt;span class='x'&gt;                            $this-&amp;gt;pager[&amp;#39;items_per_page&amp;#39;]);&lt;/span&gt;
&lt;span class='x'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As can be seen in the code above, its also possible to detect if the query failed against Akiban and re-issue it against MySQL.&lt;/p&gt;

&lt;p&gt;When deployed in this configuration, connection details for the Akiban Server is specified in the &lt;code&gt;settings.php&lt;/code&gt; file for a Drupal site.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='php'&gt;&lt;span class='x'&gt;$db_url = array(&lt;/span&gt;
&lt;span class='x'&gt;  &amp;quot;default&amp;quot; =&amp;gt; &amp;quot;mysql://drupal:drupal@mysql_hostname/drupal_schema&amp;quot;,&lt;/span&gt;
&lt;span class='x'&gt;  &amp;quot;akiban&amp;quot;  =&amp;gt; &amp;quot;pgsql://drupal:drupal@akiban_hostname:15432/drupal_schema&amp;quot;&lt;/span&gt;
&lt;span class='x'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We&amp;#8217;ve also done some work with clients where we have made patches to the Views module for Drupal. These patches allow a client to send the queries generated by a specific view to an Akiban Server.&lt;/p&gt;

&lt;p&gt;Taken together, this makes it quite easy for us to work with Drupal 6 and send problematic queries to Akiban.&lt;/p&gt;</description>
            <pubDate>Fri, 18 May 2012 00:00:00 -0700</pubDate>
            <link>http://posulliv.github.com/2012/05/18/drupal-6-akiban</link>
            <guid isPermaLink="true">http://posulliv.github.com/2012/05/18/drupal-6-akiban</guid>
          </item>
        
      
    
      
        
          <item>
            <title>Akiban Server Progress with Drupal 7</title>
            <description>&lt;p&gt;The call for papers for &lt;a href='http://munich2012.drupal.org/'&gt;DrupalCon Munich&lt;/a&gt; closed on Friday and I &lt;a href='http://munich2012.drupal.org/program/sessions/building-new-database-driver-drupal-7'&gt;submitted&lt;/a&gt; a session related to the work I&amp;#8217;m doing on developing a database module for the Akiban Server with Drupal 7. That work has not been open sourced yet but will be before August. We also plan on open sourcing and releasing the Akiban Server for public download by August as well. The end result of this work will be a database driver for the Akiban Server that will allow Drupal 7 to run on Akiban.&lt;/p&gt;

&lt;p&gt;In this post, I wanted to briefly show the type of results I&amp;#8217;ve been seeing from running Drupal on Akiban. To do this, I constructed a simple view using the Views module and benchmarked the query that resulted from this view.&lt;/p&gt;

&lt;h1 id='environment_setup'&gt;Environment Setup&lt;/h1&gt;

&lt;p&gt;All results were gathered on EC2 instances. The base AMI used for these results is an official AMI of Ubuntu 10.04 provided by &lt;a href='http://cloud-images.ubuntu.com/releases/10.04/release'&gt;Canonical&lt;/a&gt;. The particular AMI used as the base image for the results gathered in this post was &lt;a href='https://console.aws.amazon.com/ec2/home?region=us-east-1#launchAmi=ami-0baf7662'&gt;ami-0baf7662&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Images used were all launched in the US-EAST-1A availability zone and were large instance types. After launching this base image I installed MySQL 5.6 and Drupal 7.12. The steps I took to install these components along with the &lt;code&gt;my.cnf&lt;/code&gt; file I used for MySQL are outlined in this &lt;a href='https://gist.github.com/2691521'&gt;gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also created an AMI from the running instance after all the steps outlined were performed. This &lt;a href='https://console.aws.amazon.com/ec2/home?region=us-east-1#launchAmi=ami-2eef4a47'&gt;AMI&lt;/a&gt; has MySQL 5.6 installed along with Drupal 7.12 and data generated with drush.&lt;/p&gt;

&lt;p&gt;The Akiban AMI cannot be made available for general download yet since we have not open-sourced our stack as of this time. Once our stack has been open-sourced I will update this post with a link to an AMI that can be downloaded. However, if you are interested in seeing the results here for yourself, feel free to contact me and I should be able to grant access to an EC2 instance for testing.&lt;/p&gt;

&lt;h2 id='data_generation'&gt;Data Generation&lt;/h2&gt;

&lt;p&gt;I used &lt;a href='http://drupal.org/project/drush'&gt;drush&lt;/a&gt; and the &lt;a href='http://drupal.org/project/devel'&gt;devel&lt;/a&gt; modules to generate data so the view would be operating on some data. I generated the following data:&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;users&lt;/td&gt;&lt;td&gt;50000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;tags&lt;/td&gt;&lt;td&gt;1000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;vocabularies&lt;/td&gt;&lt;td&gt;5000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;menus&lt;/td&gt;&lt;td&gt;5000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;nodes&lt;/td&gt;&lt;td&gt;100000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;max comments per node&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;h1 id='view_and_sql_query'&gt;View and SQL Query&lt;/h1&gt;

&lt;p&gt;I created a simple view that filters and sorts on content criteria. A screenshot of my view construction page can be seen &lt;a href='/images/view_screen_shot.png'&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The resulting SQL query that gets executed by this view is:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sql'&gt;&lt;span class='k'&gt;SELECT&lt;/span&gt; &lt;span class='k'&gt;DISTINCT&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;title&lt;/span&gt;                            &lt;span class='k'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;node_title&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
                &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt;                              &lt;span class='k'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;nid&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
                &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;comment_count&lt;/span&gt; &lt;span class='k'&gt;AS&lt;/span&gt; 
                &lt;span class='n'&gt;node_comment_statistics_comment_count&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; 
                &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;created&lt;/span&gt;                          &lt;span class='k'&gt;AS&lt;/span&gt; &lt;span class='n'&gt;node_created&lt;/span&gt; 
&lt;span class='k'&gt;FROM&lt;/span&gt;   &lt;span class='n'&gt;node&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt; 
       &lt;span class='k'&gt;INNER&lt;/span&gt; &lt;span class='k'&gt;JOIN&lt;/span&gt; &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt; &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt; 
         &lt;span class='k'&gt;ON&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; 
&lt;span class='k'&gt;WHERE&lt;/span&gt;  &lt;span class='p'&gt;((&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;status&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;1&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
          &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='k'&gt;comment&lt;/span&gt; &lt;span class='k'&gt;IN&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;2&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
          &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;node&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nid&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;111&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; 
          &lt;span class='k'&gt;AND&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt; &lt;span class='n'&gt;node_comment_statistics&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;comment_count&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;2&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='k'&gt;ORDER&lt;/span&gt;  &lt;span class='k'&gt;BY&lt;/span&gt; &lt;span class='n'&gt;node_created&lt;/span&gt; &lt;span class='k'&gt;ASC&lt;/span&gt; 
&lt;span class='k'&gt;LIMIT&lt;/span&gt;  &lt;span class='mi'&gt;50&lt;/span&gt; &lt;span class='k'&gt;offset&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id='performance_comparison'&gt;Performance Comparison&lt;/h1&gt;

&lt;p&gt;The response time of the query in Akiban versus MySQL is shown below.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/drupal_view_response_time.png' alt='Reponse time comparison.' /&gt;
&lt;/div&gt;
&lt;p&gt;As seen in the image above, Akiban can execute the query in question in 5 ms or less whereas MySQL consistently takes 1200 ms to execute the query. In the next section I&amp;#8217;ll go into details of how Akiban executes this query.&lt;/p&gt;

&lt;p&gt;Secondly, numbers were obtained using the mysqlslap benchmark tool from MySQL to demonstrate how Akiban performs versus MySQL with varying degrees of concurrency.&lt;/p&gt;
&lt;div&gt;
  &lt;img src='/images/drupal_view_throughput.png' alt='Throughput comparison.' /&gt;
&lt;/div&gt;
&lt;h1 id='mysql_execution_plan'&gt;MySQL Execution Plan&lt;/h1&gt;

&lt;p&gt;Using Maatkit to visualize the MySQL execution plan, we get:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;JOIN
+- Filter with WHERE
|  +- Bookmark lookup
|     +- Table
|     |  table          node_comment_statistics
|     |  possible_keys  PRIMARY,comment_count
|     +- Unique index lookup
|        key            node_comment_statistics-&amp;gt;PRIMARY
|        possible_keys  PRIMARY,comment_count
|        key_len        4
|        ref            drupal.node.nid
|        rows           1
+- Table scan
   +- TEMPORARY
      table          temporary&lt;span class='o'&gt;(&lt;/span&gt;node&lt;span class='o'&gt;)&lt;/span&gt;
      +- Filter with WHERE
         +- Bookmark lookup
            +- Table
            |  table          node
            |  possible_keys  PRIMARY,node_status_type
            +- Index scan
               key            node-&amp;gt;node_created
               possible_keys  PRIMARY,node_status_type
               key_len        4
               rows           100
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;MySQL chooses to start from the node table and scans an index on the created column. A temporary table is then created in memory to store the results of this index scan. The items stored in the temporary table are then processed to eliminate duplicates (for the DISTINCT). For each distinct row in the temporary table, MySQL then performs a join to the &lt;code&gt;node_comment_statistics&lt;/code&gt; table by performing an index lookup using its primary key.&lt;/p&gt;

&lt;h1 id='akiban_execution_plan'&gt;Akiban Execution Plan&lt;/h1&gt;

&lt;p&gt;The tables involved in the query fall into a single table group in Akiban - the node group. Grouping is explained by our CTO in this &lt;a href='http://www.akiban.com/blog/2011/04/18/grouping_explained'&gt;post&lt;/a&gt; and that post includes a grouping for Drupal where you can see the node group. For this query, it means all joins within the node group are executed with essentially zero cost. It also allows for the creation of Akiban group indexes. A group index is an index that can span multiple tables along a single branch within a table group.&lt;/p&gt;

&lt;p&gt;A covering group index for this query is:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;CREATE INDEX cvr_gi ON node
&lt;span class='o'&gt;(&lt;/span&gt;
  node.status,
  node.comment,
  node.created,
  node.nid,
  node_comment_statistics.comment_count,
  node_comment_statistics.nid,
  node.title
&lt;span class='o'&gt;)&lt;/span&gt; USING LEFT JOIN
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that the &lt;code&gt;node.created&lt;/code&gt; column is included in this index so a sort could be avoided.&lt;/p&gt;

&lt;p&gt;The other large advantage Akiban brings when executing this query is the query optimizer is intelligent enough to determine that the DISTINCT is not required in the query due to the 1-to-1 mapping between &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;node_comment_statistics&lt;/code&gt; and the fact that an INNER JOIN is being performed between these 2 tables.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;Limit_Default&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;limit&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;50: project&lt;span class='o'&gt;([&lt;/span&gt;Field&lt;span class='o'&gt;(&lt;/span&gt;6&lt;span class='o'&gt;)&lt;/span&gt;, Field&lt;span class='o'&gt;(&lt;/span&gt;3&lt;span class='o'&gt;)&lt;/span&gt;, Field&lt;span class='o'&gt;(&lt;/span&gt;4&lt;span class='o'&gt;)&lt;/span&gt;, Field&lt;span class='o'&gt;(&lt;/span&gt;2&lt;span class='o'&gt;)]))&lt;/span&gt;
  project&lt;span class='o'&gt;([&lt;/span&gt;Field&lt;span class='o'&gt;(&lt;/span&gt;6&lt;span class='o'&gt;)&lt;/span&gt;, Field&lt;span class='o'&gt;(&lt;/span&gt;3&lt;span class='o'&gt;)&lt;/span&gt;, Field&lt;span class='o'&gt;(&lt;/span&gt;4&lt;span class='o'&gt;)&lt;/span&gt;, Field&lt;span class='o'&gt;(&lt;/span&gt;2&lt;span class='o'&gt;)])&lt;/span&gt;
    Select_HKeyOrdered&lt;span class='o'&gt;(&lt;/span&gt;Index&lt;span class='o'&gt;(&lt;/span&gt;cvr_gi&lt;span class='o'&gt;(&lt;/span&gt;BoolLogic&lt;span class='o'&gt;(&lt;/span&gt;AND -&amp;gt; Field&lt;span class='o'&gt;(&lt;/span&gt;3&lt;span class='o'&gt;)&lt;/span&gt; &amp;gt;&lt;span class='o'&gt;=&lt;/span&gt; Literal&lt;span class='o'&gt;(&lt;/span&gt;111&lt;span class='o'&gt;)&lt;/span&gt;, Field&lt;span class='o'&gt;(&lt;/span&gt;4&lt;span class='o'&gt;)&lt;/span&gt; &amp;gt;&lt;span class='o'&gt;=&lt;/span&gt; Literal&lt;span class='o'&gt;(&lt;/span&gt;2&lt;span class='o'&gt;)&lt;/span&gt; -&amp;gt; BOOL&lt;span class='o'&gt;))&lt;/span&gt;
      IndexScan_Default&lt;span class='o'&gt;(&lt;/span&gt;Index&lt;span class='o'&gt;(&lt;/span&gt;cvr_gi&lt;span class='o'&gt;(&lt;/span&gt;&amp;gt;&lt;span class='o'&gt;=&lt;/span&gt;UnboundExpressions&lt;span class='o'&gt;[&lt;/span&gt;Literal&lt;span class='o'&gt;(&lt;/span&gt;1&lt;span class='o'&gt;)&lt;/span&gt;, Literal&lt;span class='o'&gt;(&lt;/span&gt;2&lt;span class='o'&gt;)]&lt;/span&gt;,&amp;lt;&lt;span class='o'&gt;=&lt;/span&gt;UnboundExpressions&lt;span class='o'&gt;[&lt;/span&gt;Literal&lt;span class='o'&gt;(&lt;/span&gt;1&lt;span class='o'&gt;)&lt;/span&gt;, Literal&lt;span class='o'&gt;(&lt;/span&gt;2&lt;span class='o'&gt;)]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above execution plan is in the Akiban format. In this format, you read the plan like a tree so we start from the leaf nodes. The above plan starts with a scan of the &lt;code&gt;cvr_gi&lt;/code&gt; index using the &lt;code&gt;node.status&lt;/code&gt; and &lt;code&gt;node.comment&lt;/code&gt; predicates. It then filters rows from this scan (the &lt;code&gt;Select_HKeyOrdered&lt;/code&gt; operator performs this filtering) before limiting the results to the size of the result set requested.&lt;/p&gt;

&lt;h1 id='conclusion'&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;To wrap up, I briefly showed some of the performance benefits we are seeing when running Drupal 7 on the Akiban Server. In the not too distant future, we will be open sourcing our stack here at Akiban and providing downloads of the Akiban Server. I will also be making the database driver for the Akiban Server for Drupal 7 available for download on drupal.org once its complete.&lt;/p&gt;

&lt;p&gt;If you are interested in trying this out yourself or want to verify the results before this work becomes publically available, feel free to contact me and I should be able to set you up with access to an EC2 instance so you try if for yourself.&lt;/p&gt;</description>
            <pubDate>Mon, 14 May 2012 00:00:00 -0700</pubDate>
            <link>http://posulliv.github.com/2012/05/14/drupal-akiban</link>
            <guid isPermaLink="true">http://posulliv.github.com/2012/05/14/drupal-akiban</guid>
          </item>
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
    
      
        
      
    
      
        
      
    
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
    
      
        
      
    
      
    
      
        
      
    
      
    
      
        
      
    
      
        
      
    
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
      
        
      
    
  </channel>
</rss>
