<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>John Goulah</title>
	<atom:link href="http://blog.johngoulah.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.johngoulah.com</link>
	<description>Musings of linux, open source, cloud computing and systems</description>
	<lastBuildDate>Mon, 09 Jan 2012 21:07:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>Distributed MySQL Sleuthing on the Wire</title>
		<link>http://blog.johngoulah.com/2012/01/distributed-mysql-sleuthing-on-the-wire/</link>
		<comments>http://blog.johngoulah.com/2012/01/distributed-mysql-sleuthing-on-the-wire/#comments</comments>
		<pubDate>Mon, 09 Jan 2012 13:52:41 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Real-time Web]]></category>
		<category><![CDATA[SSH]]></category>
		<category><![CDATA[Systems]]></category>
		<category><![CDATA[distributed]]></category>
		<category><![CDATA[dsh]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[percona]]></category>
		<category><![CDATA[pt-query-digest]]></category>
		<category><![CDATA[tcpdump]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=749</guid>
		<description><![CDATA[Intro Oftentimes you need to know what MySQL is doing right now and furthermore if you are handling heavy traffic you probably have multiple instances of it running across many nodes. I&#8217;m going to start by showing how to take a tcpdump capture on one node, a few ways to analyze that, and then go [...]]]></description>
			<content:encoded><![CDATA[<h2>Intro</h2>
<p>Oftentimes you need to know what MySQL is doing <em>right now</em> and furthermore if you are handling heavy traffic you probably have multiple instances of it running across many nodes.   I&#8217;m going to start by showing how to take a <a href="http://www.tcpdump.org/" target="_blank">tcpdump</a> capture on one node,  a few ways to analyze that,  and then go into how to take a distributed capture across many nodes for aggregate analysis.</p>
<h2>Taking the Capture</h2>
<p>The first thing you need to do is to take a capture of the interesting packets.  You can either do this on the MySQL server or on the hosts talking to it.  According to this <a href="http://www.mysqlperformanceblog.com/2011/04/18/how-to-use-tcpdump-on-very-busy-hosts/" target="_blank">percona post</a> this command is the best way to capture mysql traffic on the eth0 interface and write it into mycapture.cap for later analysis:</p>
<pre>
% tcpdump -i eth0 -w mycapture.cap -s 0 "port 3306 and tcp[1] &#038; 7 == 2 and tcp[3] &#038; 7 == 2"
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
47542 packets captured
47703 packets received by filter
60 packets dropped by kernel
</pre>
<h2> Analyzing the Capture</h2>
<p>The next step is to take a look at your captured data.  One way to do this is with tshark, which is the command line part of <a href="http://www.wireshark.org/" target="_blank">wireshark</a>.  You can do <em>yum install wireshark</em> or similar to install it.  Usually you want to do this on a different host than the one taking traffic since it can be memory and CPU intensive.</p>
<p>You can then use it to reconstruct the mysql packets like so:</p>
<pre>
% tshark -d tcp.port==3306,mysql -T fields -R mysql.query -e frame.time -e ip.src -e ip.dst -e mysql.query -r mycapture.cap
</pre>
<p>This will give you the time, source IP, destination IP, and query but this is still really raw output.  Its a nice start but we can do better.  Percona has released the <a href="http://www.percona.com/software/percona-toolkit/" target="_blank">Percona Toolkit</a> which includes some really nice command line tools (including what used to be in Maatkit). </p>
<p>The one we&#8217;re interested in here is <a href="http://www.percona.com/doc/percona-toolkit/2.0/pt-query-digest.html">pt-query-digest</a></p>
<p>It has tons of options and you should read the documentation,  but here&#8217;s a few I&#8217;ve used recently.</p>
<p>Lets say you want to get the top tables queried from your tcpdump</p>
<pre>
% tcpdump -r mycapture.cap -n -x -q -tttt | pt-query-digest --type tcpdump --group-by tables --order-by Query_time:cnt \
 --report-format profile --limit 5
reading from file mycapture.cap, link-type EN10MB (Ethernet)

# Profile
# Rank Query ID Response time Calls R/Call Apdx V/M   Item
# ==== ======== ============= ===== ====== ==== ===== ====================
#    1 0x        0.3140  6.1%   674 0.0005 1.00  0.00 shard.images
#    2 0x        0.8840 17.1%   499 0.0018 1.00  0.03 shard.activity
#    3 0x        0.1575  3.1%   266 0.0006 1.00  0.00 shard.listing_images
#    4 0x        0.1680  3.3%   265 0.0006 1.00  0.00 shard.connection_edges_reverse
#    5 0x        0.0598  1.2%   254 0.0002 1.00  0.00 shard.listing_translations
# MISC 0xMISC    3.5771 69.3%  3534 0.0010   NS   0.0 <86 ITEMS>
</pre>
<p>Note the tcpdump options I used this time,  which the tool requires to work properly when passing <em>&#8211;type tcpdump</em>.   I also grouped by tables (as opposed to full queries) and ordered by the count (the Calls column).  It will stop at your <em>&#8211;limit</em> and group the rest into MISC so be aware of that.</p>
<p>You can remove the <em>&#8211;order-by</em> to sort by response time, which is the default sort order, or provide other attributes to sort on.  We can also change the <em>&#8211;report-format</em>,  for example to <em>header</em>:</p>
<pre>
% tcpdump -r mycapture.cap -n -x -q -tttt | pt-query-digest --type tcpdump --group-by tables --report-format header
reading from file mycapture.cap, link-type EN10MB (Ethernet)

# Overall: 5.49k total, 91 unique, 321.13 QPS, 0.30x concurrency _________
# Time range: 2012-01-08 15:52:05.814608 to 15:52:22.916873
# Attribute          total     min     max     avg     95%  stddev  median
# ============     ======= ======= ======= ======= ======= ======= =======
# Exec time             5s     3us   114ms   939us     2ms     3ms   348us
# Rows affecte         316       0      13    0.06    0.99    0.29       0
# Query size         3.64M      18   5.65k  694.98   1.09k  386.68  592.07
# Warning coun           0       0       0       0       0       0       0
# Boolean:
# No index use   0% yes,  99% no
</pre>
<p>If you set the <em>&#8211;report-format</em> to <em>query_report</em> you will get gobs of verbose information that you can dive into and you can use the <em>&#8211;filter</em> option to do things like getting slow queries:</p>
<pre>
% tcpdump -r mycapture.cap -n -x -q -tttt | \
  pt-query-digest --type tcpdump --filter '($event->{No_index_used} eq "Yes" || $event->{No_good_index_used} eq "Yes")'
</pre>
<h2>Distributed Capture</h2>
<p>Now that we&#8217;ve taken a look at capturing and analyzing packets from one host,  its time to dive into looking at our results across the cluster.  The main trick is that tcpdump provides no option to stop capturing &#8211; you have to explicitly kill it.  Otherwise we&#8217;ll just use <a href="http://www.netfort.gr.jp/~dancer/software/dsh.html.en">dsh</a> to send our commands out.  We&#8217;ll assume you have a user that can hop around in a password-less fashion using ssh keys &#8211; setting that up is well outside the scope of this article but there&#8217;s plenty of info out there on how to do that. </p>
<p>There&#8217;s a few ways you can let a process run on a &#8220;timeout&#8221; but I&#8217;m assuming we don&#8217;t have any script written or tools like <a href="http://www.bashcookbook.com/bashinfo/source/bash-4.0/examples/scripts/timeout3" target="_blank">bash timeout</a> or the one distributed in coreutils available.</p>
<p>So we&#8217;re going off the premise that you will background the process and kill it after a sleep by grabbing its pid:</p>
<pre>
( /path/to/command with options ) &#038; sleep 5 ; kill $!
</pre>
<p>Simple enough,  except we&#8217;ll want to capture the output on each host, so we need to ssh the output back over to the target using a pipe to grab the stdout.  This means that $! will return the pid of our ssh command instead of our tcpdump command.  We end up having to do a little trick to kill the right process, since the capture won&#8217;t be readable if we kill ssh command that is writing the output.  We&#8217;ll need to kill tcpdump and to do that we can look at the parent pid of the ssh process,  ask pkill (similar to pgrep) for all of the processes that have this parent,  and finally kill the oldest one,  which ends up being our tcpdump process.</p>
<p>Then end result looks like this if I were to run it across two machines:</p>
<pre>
% dsh -c -m web1000,web1001 \
   'sudo /usr/sbin/tcpdump -i eth0 -w - -s 0 -x -n -q -tttt "port 3306 and tcp[1] &#038; 7 == 2 and tcp[3] &#038; 7 == 2" | \
   ssh dshhost "cat - &gt; ~/captures/$(hostname -a).cap" &#038; sleep 10 ; \
   sudo pkill -o -P $(ps -ef | awk "\$2 ~ /\&lt;$!\&gt;/ { print \$3; }")'
</pre>
<p>So this issues a dsh to two of our hosts (you can make a dsh group with 100 or 1000 hosts though) and runs the command concurrently on each (-c).  We issue our tcpdump on each target machine and send the output to stdout for ssh to then cat back to a directory on the source machine that issued the dsh.  This way we have all of our captures in one directory with each file named with the target name of each host the tcpdump was run.   The sleep is how long the dump is going to run for before we then kill off the tcpdump.</p>
<p>The last piece of the puzzle is to get these all into one file and we can use the mergecap tool for this, which is also part of wireshark:</p>
<pre>
% /usr/sbin/mergecap -F libpcap -w output.cap *.cap
</pre>
<p>And then we can analyze it like we did above.</p>
<h2>Further Reading</h2>
<h3>References</h3>
<p><a href="http://www.mysqlperformanceblog.com/2011/04/18/how-to-use-tcpdump-on-very-busy-hosts" target="_blank">http://www.mysqlperformanceblog.com/2011/04/18/how-to-use-tcpdump-on-very-busy-hosts</a></p>
<p><a href="http://stackoverflow.com/questions/687948/timeout-a-command-in-bash-without-unnecessary-delay" target="_blank">http://stackoverflow.com/questions/687948/timeout-a-command-in-bash-without-unnecessary-delay</a></p>
<p><a href="http://www.xaprb.com/blog/2009/08/18/how-to-find-un-indexed-queries-in-mysql-without-using-the-log/" target="_blank">http://www.xaprb.com/blog/2009/08/18/how-to-find-un-indexed-queries-in-mysql-without-using-the-log/</a></p>
<h3>Breaking the distributed command down further</h3>
<p>Just to clarify this command a bit more, particularly how the kill part works since that was the trickiest part for me to figure out.</p>
<p>When we run this</p>
<pre>
$ dsh -c -m web1000,web1001 \
   'sudo /usr/sbin/tcpdump -i eth0 -w - -s 0 -x -n -q -tttt "port 3306 and tcp[1] &#038; 7 == 2 and tcp[3] &#038; 7 == 2" | \
   ssh dshhost "cat - &gt; ~/captures/$(hostname -a).cap" &#038; sleep 10 ; \
   sudo pkill -o -P $(ps -ef | awk "\$2 ~ /\&lt;$!\&gt;/ { print \$3; }")'
</pre>
<p>on the server the process list looks something like</p>
<pre>
user     12505 12504  0 03:12 ?        00:00:00 bash -c sudo /usr/sbin/tcpdump -i eth0 -w - -s 0 -x -n -q -tttt "port 3306 and tcp[1] &#038; 7 == 2 and tcp[3] &#038; 7 == 2" | ssh myhost.myserver.com "cat - > /home/etsy/captures/$(hostname -a).cap" &#038; sleep 5 ; sudo pkill -o -P $(ps -ef | awk "\$2 ~ /\<$!\>/ { print \$3; }")
pcap     12506 12505  1 03:12 ?        00:00:00 /usr/sbin/tcpdump -i eth0 -w - -s 0 -x -n -q -tttt port 3306 and tcp[1] &#038; 7 == 2 and tcp[3] &#038; 7 == 2
user     12507 12505  0 03:12 ?        00:00:00 ssh myhost.myserver.com cat - > ~/captures/web1001.cap
</pre>
<p>So $! is going to return the pid of the ssh process, 12507.  We use awk to find the process matching that, and then print the parent pid out,  which is then passed to the -P arg of pkill.   If you use pgrep to look at this without the -o you&#8217;d get a list of the children of 12505,  which are 12506 and 12507.   The oldest child is the tcpdump command and so adding -o kills that guy off.</p>
<p>So if we were only running the command on one host we could use something much simpler</p>
<pre>
ssh dbhost01 '(sudo /usr/sbin/tcpdump -i eth0 -w - -s 0 port 3306) &#038; sleep 10; sudo kill $!' | cat - &gt; output.cap
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2012/01/distributed-mysql-sleuthing-on-the-wire/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>iOS Development with Vim and the Command Line</title>
		<link>http://blog.johngoulah.com/2011/12/ios-development-with-vim/</link>
		<comments>http://blog.johngoulah.com/2011/12/ios-development-with-vim/#comments</comments>
		<pubDate>Fri, 02 Dec 2011 04:12:33 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[ctags]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[objective-c]]></category>
		<category><![CDATA[vim]]></category>
		<category><![CDATA[xcode]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=712</guid>
		<description><![CDATA[Intro I&#8217;ve recently been playing around with some iOS code, and while I find Xcode an excellent editor with a lot of great built-ins, I&#8217;m a vim user at heart. It&#8217;s the editor I&#8217;ve been using on a daily basis for years, so its tough to switch when I want to do iPhone development. Of [...]]]></description>
			<content:encoded><![CDATA[<h2>Intro</h2>
<p>I&#8217;ve recently been playing around with some iOS code, and while I find Xcode an excellent editor with a lot of great built-ins,  I&#8217;m a vim user at heart.  It&#8217;s the editor I&#8217;ve been using on a daily basis for years,  so its tough to switch when I want to do iPhone development.  Of course there are some things you just have to use Xcode for,  but I&#8217;ve been able to tailor vim to do most of my dev work,  as well as build and run the simulator on the command line.  I couldn&#8217;t find a single cohesive tutorial on this,  so these are the pieces I put together to make this work for me.</p>
<h2>Editing</h2>
<h3>The Vim Cocoa Plugin</h3>
<p>To start, there is a decent plugin called <a href="http://www.vim.org/scripts/script.php?script_id=2674" target="_blank">cocoa.vim</a> to get some of the basics going.  It attempts to do a lot of things but I&#8217;m only using it for a few specific things &#8211; the syntax highlighting,  the method list, and the documentation browse functionality. </p>
<p>One caveat is the plugin is not very well maintained,  given the last release is nearly 2 years ago.  Because of this you&#8217;ll want to grab the version from my github:</p>
<pre>git clone https://jgoulah@github.com/jgoulah/cocoa.vim.git</pre>
<p>Install it by following the simple instructions in the <a href="https://github.com/jgoulah/cocoa.vim/blob/master/README.markdown" target="_blank">README</a>.</p>
<p>You&#8217;ll get the syntax highlighting by default, and the method listing is pretty straightforward.  While you have a file open you can type <em>:ListMethods</em> and navigate the methods in the file.   Of course you may want to map this to a key combo,  here&#8217;s what I have in my .vimrc for this, but of course you can map to whatever you like:</p>
<pre>
map &lt;leader&gt;l :ListMethods<CR>
</pre>
<p>There&#8217;s another command that you can use called <em>:CocoaDoc</em> that will search the documentation for the keyword and open it in your browser.  For example, you can type <em>:CocoaDoc NSString</em> to open the documentation for NSString in your browser.  However OS X gives a warning every time,  which is pretty annoying.  You can disable this on the command line:</p>
<pre>
defaults write com.apple.LaunchServices LSQuarantine -bool NO
</pre>
<p>After you&#8217;ve run the command,  restart your Finder (or reboot).  Lastly,  it would be annoying to type the keyword every time,  so you can configure a mapping in your .vimrc so that it will try to launch the documentation for the word the cursor is on:</p>
<pre>
map &lt;leader&gt;d :exec("CocoaDoc ".expand("&lt;cword&gt;"))&lt;CR&gt;
</pre>
<h3>Navigation</h3>
<p>One of the biggest things I miss when I&#8217;m in Xcode is the functionality you get from ctags.  Of course you can right click on a function and &#8220;jump to definition&#8221;.  Works fine, but feels clunky to me.  I love being able to hop through code quickly with ctags commands.   I&#8217;ve <a href="http://blog.johngoulah.com/2009/04/make-browsing-code-easier/" target="_blank">talked about this before</a>, so I&#8217;m not going to give a full tutorial about ctags,  but I&#8217;ll quickly go over how I made it work for my code. </p>
<p>First go ahead and grab the version from github that includes Objective-C support:</p>
<pre>
git clone https://github.com/mcormier/ctags-ObjC-5.8.1
</pre>
<p>Compile and install in the usual way.   Now you can utilize it to generate yourself a tags file.  Mine looks like this:</p>
<div id="gist-1421612" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="c">#!/bin/bash</span></div><div class='line' id='LC2'><span class="nb">cd</span> ~/wdir</div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'><span class="k">for </span>i in MyApp ios_frameworks ; <span class="k">do</span></div><div class='line' id='LC5'><span class="k">    </span><span class="nb">echo</span> <span class="s2">&quot;tagging $i&quot;</span></div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">pushd</span> <span class="nv">$i</span></div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;/usr/local/bin/ctags -f ~/.vimtags/<span class="nv">$i</span> -R <span class="se">\</span></div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;--exclude<span class="o">=</span><span class="s1">&#39;.git&#39;</span> <span class="se">\</span></div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;--langmap<span class="o">=</span>objc:.m.h <span class="se">\</span></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;--totals<span class="o">=</span>yes <span class="se">\</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;--tag-relative<span class="o">=</span>yes <span class="se">\</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;--regex-objc<span class="o">=</span><span class="s1">&#39;/^[[:space:]]*[-+][[:space:]]*\([[:alpha:]]+[[:space:]]*\*?\)[[:space:]]*([[:alnum:]]+):[[:space:]]*\(/\1/m,method/&#39;</span> <span class="se">\</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;--regex-objc<span class="o">=</span><span class="s1">&#39;/^[[:space:]]*[-+][[:space:]]*\([[:alpha:]]+[[:space:]]*\*?\)[[:space:]]*([[:alnum:]]+)[[:space:]]*\{/\1/m,method/&#39;</span> <span class="se">\</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;--regex-objc<span class="o">=</span><span class="s1">&#39;/^[[:space:]]*[-+][[:space:]]*\([[:alpha:]]+[[:space:]]*\*?\)[[:space:]]*([[:alnum:]]+)[[:space:]]*\;/\1/m,method/&#39;</span> <span class="se">\</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;--regex-objc<span class="o">=</span><span class="s1">&#39;/^[[:space:]]*\@property[[:space:]]+.*[[:space:]]+\*?(.*);$/\1/p,property/&#39;</span> <span class="se">\</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;--regex-objc<span class="o">=</span><span class="s1">&#39;/^[[:space:]]*\@implementation[[:space:]]+(.*)$/\1/c,class/&#39;</span> <span class="se">\</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;--regex-objc<span class="o">=</span><span class="s1">&#39;/^[[:space:]]*\@interface[[:space:]]+(.*)[[:space:]]+:.*{/\1/i,interface/&#39;</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">popd</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'><span class="k">done</span>;</div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1421612/289e5c287012d72ff18eb6de8c9cad8e26b27f8f/gistfile1.sh" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1421612#file_gistfile1.sh" style="float:right;margin-right:10px;color:#666">gistfile1.sh</a>
            <a href="https://gist.github.com/1421612">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Just a little explanation here,  I&#8217;m going into my working directory and generating a tags file for both MyApp and ios_frameworks.  ios_frameworks is just a symlink that points to <em>/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks</em></p>
<p>If you&#8217;re interested in how I load up these tag files you can check out my <a href="https://github.com/jgoulah/dotfiles/blob/master/vimrc#L178" target="_blank">.vimrc on github</a>.</p>
<p>Another handy feature from I wanted to emulate was flipping between the header and source file.  Luckily there is yet another vim plugin that will do this for you called <a href="http://vim.sourceforge.net/scripts/script.php?script_id=31" target="_blank">a.vim</a>.</p>
<p>Grab it off github and install it by dropping it in your <em>~/.vim/plugin</em> directory:</p>
<pre>
git clone https://github.com/vim-scripts/a.vim.git
cp a.vim/plugin/a.vim  ~/.vim/plugin
</pre>
<p>There&#8217;s a tiny bit of configuration that you can put in <em>.vim/ftplugin/objc.vim</em>:</p>
<pre>
let g:alternateExtensions_m = "h"
let g:alternateExtensions_h = "m"
map &lt;leader&gt;s :A<CR>
</pre>
<p>This just tells it which files to use for source and header files and creates another shortcut for us to swap between the two.</p>
<h2>Build and Run</h2>
<p>So now that we&#8217;ve got most of the basic things that help us edit code,  its time to build the code.  You can use xcodebuild for this on the command line.  If you type <em>xcodebuild &#8211;usage</em> you can see the variety of options.  Here&#8217;s what worked for me to build my target app, noting that I setup everything in Xcode first and made sure it built there. After that you can just specify the target, sdk, and configuration.  This will create a build under the Debug configuration for the iPhone simulator:</p>
<pre>
xcodebuild -target "MyApp Enterprise" -configuration Debug -project MyApp.xcodeproj -sdk iphonesimulator5.0
</pre>
<p>And now to run the app.  Back to github there&#8217;s a nice little app called <a href="https://github.com/jhaynie/iphonesim" target="_blank">iphonesim</a>.  Download that and just build it with xcode:</p>
<pre>
git clone https://github.com/jhaynie/iphonesim.git
open iphonesim.xcodeproj
</pre>
<p>Put the resulting binary somewhere in your PATH like <em>/usr/bin</em>.</p>
<p>Now we can launch the app we just built in the simulator:</p>
<pre>
iphonesim launch /Users/jgoulah/wdir/MyApp/build/Debug-iphonesimulator/MyAppEnterprise.app
</pre>
<p>Last thing you&#8217;ll want are logs.  Another thing that is arguably more easily accessible in Xcode, but we actually <em>can</em> get the console log.  If you&#8217;re using something like NSLog to debug this will grab all of that output.  You&#8217;ll have to add a couple lines to your <em>main.m</em> source file:</p>
<pre>
NSString *logPath = @"/some/path/to/output.log";
freopen([logPath fileSystemRepresentation], "a", stderr);
</pre>
<p>And then you can run <em>tail -f /some/path/to/output.log</em></p>
<h2> Conclusion </h2>
<p>This is how I use vim to compile iPhone apps.  Your mileage may vary and there are many ways to do this,  it just happens to be the path that worked well for me without having to pull my hair out too much.  Xcode is a great editor and there are some things that you will have to use it for such as the debugging capabilities and running the app on the phone itself.  Of course even these are possible with some work, so if anyone has tips feel free to leave a comment.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2011/12/ios-development-with-vim/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hacking on Backbone.js and Rdio API to Build a Playlist App</title>
		<link>http://blog.johngoulah.com/2011/09/hacking-backbone-and-rdio/</link>
		<comments>http://blog.johngoulah.com/2011/09/hacking-backbone-and-rdio/#comments</comments>
		<pubDate>Sun, 25 Sep 2011 16:47:20 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=692</guid>
		<description><![CDATA[Intro I&#8217;ve been meaning to play around with backbone.js for a while,  trying to piece together what can make a sensible framework for frontend code. There seem to be a few out there, another I&#8217;ve wanted to try is sproutcore.  The truth is, I&#8217;m a backend coder, a database guy, a systems engineer&#8230; but I [...]]]></description>
			<content:encoded><![CDATA[<h2>Intro</h2>
<p>I&#8217;ve been meaning to play around with <a title="backbone.js" href="http://documentcloud.github.com/backbone/" target="_blank">backbone.js</a> for a while,  trying to piece together what can make a sensible framework for frontend code. There seem to be a few out there, another I&#8217;ve wanted to try is <a title="sproutcore" href="http://www.sproutcore.com/" target="_blank">sproutcore</a>.  The truth is,  I&#8217;m a backend coder, a database guy, a systems engineer&#8230; but I love all aspects of coding, and although I&#8217;m quite terrible at design and CSS,  I&#8217;ve been getting more into Javascript lately.  Its quite a nice language for server side development with <a title="node.js" href="http://nodejs.org/" target="_blank">node</a> &#8211; small, simple, server based apps that work well with websockets.  I&#8217;ve written a little bit about that in the <a title="websockets" href="http://blog.johngoulah.com/2010/03/nodejs-websockets-and-the-twitter-gardenhose/" target="_blank">past</a>.  Everything is moving to the web, and its nice to have a modern and easy to use interface to place in front of your tools.</p>
<p>The app is a simple playlist.  You can search for songs,  get a list of potential matches,  and add the song to your playlist.  You can cycle back and forth through the songs, pause them, and delete them from the list.  Thats about it!</p>
<p>If you want to cut to the code you can find it <a title="github" href="https://github.com/jgoulah/playlister" target="_blank">here on github</a>.</p>
<h2>Some Assumptions</h2>
<p>Part of the goals for this project were to get out of my comfort zone and learn something new.  So I decided to make it a frontend only app.  All HTML/Javascript and no backend services.  Well,  none that I write,  it does some interaction with other services or it would be pretty boring.  If you end up checking the <a title="playlister" href="https://github.com/jgoulah/playlister" target="_blank">project</a> out, you&#8217;ll find the markup could use some help (patches welcome!).  But the goal was just to get something functional.  This isn&#8217;t the way I&#8217;d go about things if I were building an app to start a company with,  its just a fun hack.</p>
<p>I didn&#8217;t know backbone at all, and it seems hard to find good examples that are more than &#8220;heres a text box on a page&#8221;.  One of the better ones out there that is well documented is the <a title="todo list" href="http://documentcloud.github.com/backbone/docs/todos.html" target="_blank">todo list</a> app.  It turns out this app fit rather nicely with my idea of a playlist,  so I decided to start off with that codebase and modify it from there. It also uses the handy <a title="localstorage" href="http://documentcloud.github.com/backbone/docs/backbone-localstorage.html" target="_blank">LocalStorage driver</a>, which implements an HTML5 <a title="webstorage spec" href="http://dev.w3.org/html5/webstorage/" target="_blank">specification</a> for storing structured data on the client side.</p>
<p>I also needed a simple way to query songs,  and since the Rdio API is Oauth based, which can get complicated (and probably more so in javascript taking security into account) I found a nice API from <a title="echonest" href="http://developer.echonest.com/" target="_blank">echonest</a> that turns out to have a lot of nice functionality thats easy to use.  This is what I&#8217;m using to query for the songs.</p>
<p>I am using the <a title="Rdio Playback API" href="http://developer.rdio.com/docs/read/Web_Playback_API" target="_blank">Rdio playback API</a> to actually play the songs from the Rdio service, and that part is based largely on their <a title="hello web playback" href="https://github.com/rdio/hello-web-playback" target="_blank">hello web playback</a> example.  Their API does allow you to build playlists in their service, which is probably the more correct way to do this since you&#8217;d be able to access them in their web UI,  but since that adds complexity thats not used in this version.</p>
<h2>The App</h2>
<p>So check out <a title="playlister" href="https://github.com/jgoulah/playlister" target="_blank">playlister</a>,  let me know what you think.  There&#8217;s a lot of room for improvement,  especially with the markup and how some of the pieces fit together.   All of the backbone code is in one file and that should be remedied.   It could also be made to sync to a backend instead of localstorage and save the playlist into Rdio,  and things like that.  When you download it,  just plug in your API keys into the js/token.js and you&#8217;re pretty much ready to go.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2011/09/hacking-backbone-and-rdio/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Deployinator: Put a Button on it</title>
		<link>http://blog.johngoulah.com/2011/07/deployinator/</link>
		<comments>http://blog.johngoulah.com/2011/07/deployinator/#comments</comments>
		<pubDate>Sun, 31 Jul 2011 00:07:35 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Deployment]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[continuous]]></category>
		<category><![CDATA[deployinator]]></category>
		<category><![CDATA[etsy]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[integration]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[OSCON]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=667</guid>
		<description><![CDATA[I&#8217;ve been meaning to post more, but have been so busy lately at Etsy its been hard to find time. Going to get better at that! In the meantime, Erik Kastner and I just got back from OSCON and gave a talk about removing the cultural barriers that come as a result of unecessary process. [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been meaning to post more,  but have been so busy lately at <a href="http://www.etsy.com/" target="_blank">Etsy</a> its been hard to find time.  Going to get better at that!  In the meantime,  <a href="https://twitter.com/#!/kastner" target="_blank">Erik Kastner</a> and I just got back from <a href="http://www.oscon.com/oscon2011" target="_blank">OSCON</a> and gave a talk about removing the cultural barriers that come as a result of unecessary process.</p>
<p>We also open sourced the tool we use to deploy code over 30 times a day <a href="http://github.com/etsy/deployinator" target="_blank">here on github</a></p>
<p>If you&#8217;re interested, here are the slides from our talk:</p>
<div style="width:425px" id="__ss_8727786"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/OReillyOSCON/put-a-button-on-it-removing-barriers-to-going-fast" title="Put a Button on It: Removing Barriers to Going Fast">Put a Button on It: Removing Barriers to Going Fast</a></strong><object id="__sse8727786" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=oscon-depoyinatornonotes-110729153209-phpapp02&#038;stripped_title=put-a-button-on-it-removing-barriers-to-going-fast&#038;userName=OReillyOSCON" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse8727786" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=oscon-depoyinatornonotes-110729153209-phpapp02&#038;stripped_title=put-a-button-on-it-removing-barriers-to-going-fast&#038;userName=OReillyOSCON" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object></div>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2011/07/deployinator/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick Tip: Dynamically Updating Screen Window Titles With The Current Server Hostname</title>
		<link>http://blog.johngoulah.com/2010/10/update-screen-window-titles-dynamically/</link>
		<comments>http://blog.johngoulah.com/2010/10/update-screen-window-titles-dynamically/#comments</comments>
		<pubDate>Sat, 02 Oct 2010 17:13:05 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Organization]]></category>
		<category><![CDATA[SSH]]></category>
		<category><![CDATA[Systems]]></category>
		<category><![CDATA[hostname]]></category>
		<category><![CDATA[screen]]></category>
		<category><![CDATA[window titles]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=636</guid>
		<description><![CDATA[I haven&#8217;t had a ton of time for blogging lately but figured this tip was good enough to throw out there for all the screen users. One way I like to organize servers that I&#8217;m ssh&#8217;d into is using screen windows. As you hopefully know you can use Ctrl-A c to create sub windows within [...]]]></description>
			<content:encoded><![CDATA[<p>I haven&#8217;t had a ton of time for blogging lately but figured this tip was good enough to throw out there for all the <a href="http://en.wikipedia.org/wiki/GNU_Screen" target="_blank">screen</a> users.  One way I like to organize servers that I&#8217;m ssh&#8217;d into is using screen windows.  As you hopefully know you can use <em>Ctrl-A  c</em> to create sub windows within screen.  Then you can switch between them in several ways such as using <em>Ctrl-A  X</em> where X is the window number,  <em>Ctrl-A  n</em> or <em>Ctrl-A  p</em> for next and previous,  and <em>Ctrl-A  &#8220;</em> to get a list of the windows for selection.  You can move windows around with <em>Ctrl-A :</em> then type <em>number X</em> where X is the window you want to swap with.  Finally, you can also name the windows with <em>Ctrl-A  A</em>.   So usually I ssh into a server,  and manually change the window title to the server name I&#8217;m ssh&#8217;d into so that its easy to organize and remember where my windows are.</p>
<p>Turns out thats a lot of repetitive work that can easily be scripted.  You can create a really simple script called <em>ssh-to</em> and place in your <em>~/bin</em> or somewhere in your path</p>
<pre>
#!/bin/bash

hostname=`basename $0`

# set screen title to short hostname
echo -n -e "\033k${hostname%%.*}\033\134"

# ssh to the server with any additional args
ssh -X $hostname $*

# set hostname back when session is done (-s on osx)
echo -n -e "\033k`hostname -s`\033\134"
</pre>
<p>Now, create symbolic links to the script with each server hostname that you use.  This can be a little tedious but you only have to do it once and the benefit is that you can now tab complete ssh&#8217;ing into servers.  </p>
<p>For example you would do something like<br />
<code><br />
 ln -s ssh-to myserver.com<br />
</code></p>
<p>Then anytime you type &#8220;myserver.com&#8221; the tab completion can fill it out,  you&#8217;re ssh&#8217;d into the server (assuming you have keys setup you don&#8217;t have to type a password) and your screen window title is updated with the domain info trimmed off, in this case <em>myserver</em>.  Now your screen windows update their own titles anytime you ssh into a new server!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2010/10/update-screen-window-titles-dynamically/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick Tip:  Gist From the Command Line</title>
		<link>http://blog.johngoulah.com/2010/05/gist-from-the-command-line/</link>
		<comments>http://blog.johngoulah.com/2010/05/gist-from-the-command-line/#comments</comments>
		<pubDate>Sat, 01 May 2010 20:56:38 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Organization]]></category>
		<category><![CDATA[command line]]></category>
		<category><![CDATA[gist]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[nopaste]]></category>
		<category><![CDATA[pastebin]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=622</guid>
		<description><![CDATA[Many a time while working it is convenient to quickly show someone else code or a chunk of output from some command. The easiest way to do this is through a pastebin service. It&#8217;s also pretty much mandated on IRC to use a service like this and there are literally thousands out there to choose [...]]]></description>
			<content:encoded><![CDATA[<p>Many a time while working it is convenient to quickly show someone else code or a chunk of output from some command.  The easiest way to do this is through a <a href="http://en.wikipedia.org/wiki/Pastebin" target="_blank">pastebin</a> service.  It&#8217;s also pretty much mandated on IRC to use a service like this and there are literally thousands out there to choose from.   </p>
<p>The <a href="http://gist.github.com/" target="_blank">gist</a> service on <a href="https://github.com/" target="_blank">github</a> is actually fairly convenient especially if you have a github account since it keeps an archive of all of your previous gists.   It also does a good job formatting and lets you paste privately.   We&#8217;ll use the <a href="http://search.cpan.org/~sartak/App-Nopaste-0.21/" target="_blank">App::Nopaste</a> tool to paste a gist straight from the command line.</p>
<p>First off install the tool from cpan</p>
<pre>$ cpan App::Nopaste</pre>
<p>You can use this tool anonymously but if you want to keep an archive of your pastes you can simply setup your git credentials in your <em>.gitconfig</em> file.  The token here is your API token which can be found under your account settings page.</p>
<pre>
[github]
        user = jgoulah
        token = 00000000000000000000000000000000
</pre>
<p>You also need either Git installed or Config::INI::Reader to allow the module to read your <em>.gitconfig</em> file.</p>
<p>Now to make this a bit easier to remember we can create an alias in our <em>.bashrc</em> file.  In this case I&#8217;m specifying <em>&#8211;private</em> so that only people that I give this secure URL out to can see,  and I&#8217;m also specifying to use the Gist service.   The nopaste app supports a variety of other services that you can use but as of this writing Gist is the only one that supports the <em>&#8211;private</em> flag. </p>
<pre>alias gist='nopaste --private --service Gist'</pre>
<p>Now you can use the command to paste something such as a script from the command line and the gist URL is returned</p>
<pre>
$ gist somescript.sh

http://gist.github.com/df552bacbbbb8d70c089
</pre>
<p>There you have it!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2010/05/gist-from-the-command-line/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Node.js, Websockets, and the Twitter Gardenhose</title>
		<link>http://blog.johngoulah.com/2010/03/nodejs-websockets-and-the-twitter-gardenhose/</link>
		<comments>http://blog.johngoulah.com/2010/03/nodejs-websockets-and-the-twitter-gardenhose/#comments</comments>
		<pubDate>Mon, 15 Mar 2010 20:08:55 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Real-time Web]]></category>
		<category><![CDATA[asynchronous]]></category>
		<category><![CDATA[commonjs]]></category>
		<category><![CDATA[evented]]></category>
		<category><![CDATA[firehose]]></category>
		<category><![CDATA[gardenhose]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[node]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[streaming]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[V8]]></category>
		<category><![CDATA[websockets]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=559</guid>
		<description><![CDATA[Introduction In this article I&#8217;m going to demonstrate how to use the Twitter Streaming API to send a stream of status updates for real time display in a web browser using Web Sockets. We&#8217;ll implement a backend server module that streams the events over a web socket using the node.websocket.js framework, which provides a simple [...]]]></description>
			<content:encoded><![CDATA[<h2>Introduction</h2>
<p>In this article I&#8217;m going to demonstrate how to use the <a href="http://apiwiki.twitter.com/Streaming-API-Documentation"  target="_blank">Twitter Streaming API</a> to send a stream of status updates for real time display in a web browser using <a href="http://en.wikipedia.org/wiki/Web_Sockets"  target="_blank">Web Sockets</a>.   We&#8217;ll implement a backend server module that streams the events over a web socket using the <a href="http://github.com/guille/node.websocket.js"  target="_blank">node.websocket.js</a> framework,  which provides a simple realtime HTTP server that implements the <a href="http://dev.w3.org/html5/websockets/"  target="_blank">web socket protocol</a>.   Node.websocket.js is built on top of <a href="http://nodejs.org"  target="_blank">Node.js</a> which provides an <a href="http://en.wikipedia.org/wiki/Asynchronous_I/O"  target="_blank">Asynchronous</a> API using callbacks.   Node.js is a server side javascript library that enforces an event driven style of programming which allows you to develop non-blocking code easily.  This in turn allows you to write simple servers that are very CPU and Memory efficient because you don&#8217;t have multiple threads taking up shared resources.  Node.js is implemented using Google&#8217;s <a href="http://code.google.com/p/v8"  target="_blank">V8</a> javascript engine and also the <a href="http://www.commonjs.org"  target="_blank">CommonJS specification</a> which is <a href="http://wiki.commonjs.org/wiki/CommonJS"  target="_blank">defining</a> a standard library for server side javascript. </p>
<h2>Getting Started</h2>
<p>First things first, you need <a href="http://github.com/ry/node" target="_blank">node.js</a>,  which you can get from github.   Install in the usual way</p>
<pre>
$ git clone git://github.com/ry/node.git
$ cd node
$ ./configure
$ make
$ sudo make install
</pre>
<p>Then we&#8217;ll need <a href="http://github.com/guille/node.websocket.js/" target="_blank">node.websocket.js</a> which you can also get from github</p>
<pre>
$ git clone git://github.com/Guille/node.websocket.js.git
</pre>
<p>This is basically an experimental implementation of the web socket API.  You can create simple server side modules and then a client side implementation that opens up a web socket to the server in which you can then exchange data in both directions.  There are a few examples included that you can take a look at including a simple echo server and a chat server.   Just fire up the server like so</p>
<pre>$ node runserver.js</pre>
<p>and then open up one of the html files in the <em>test/websocket/</em> directory in your browser.   One catch though,  you&#8217;ll need a current browser such as <a href="http://www.google.com/chrome" target="_blank">Google Chrome</a>.   I&#8217;d recommend it anyway, as browsing the web with it does run a bit smoother.  </p>
<p>You&#8217;ll also need <a href="http://curl.haxx.se/" target="_blank">curl</a>,  which is pretty common on any linux box these days and can be installed through your package manager.</p>
<h2>Setting Up the Server</h2>
<p>The way we&#8217;re going to implement the server is to use the curl command to pull from the twitter stream into a file.  Twitter gives you a bunch of JSON objects back which you can then parse and display.  We&#8217;ll use this file in a moment to send the data over the web socket to the browser.  There is some <a href="http://apiwiki.twitter.com/Streaming-API-Documentation" target="_blank">documentation</a> on what you can do with the API but we&#8217;ll keep it simple and search for any tweets with &#8216;nyc&#8217; in them</p>
<pre>$ curl -dtrack=nyc http://stream.twitter.com/1/statuses/filter.json  -uUSERNAME:PASSWORD > sample.json</pre>
<p>Now its time to write some server side javascript.  In the node.websocket.js checkout there is a <em>modules/</em> directory. We&#8217;ll create a new module called <em>gardenhose.js</em>.   So the full path is <em>node.websocket.js/modules/gardenhose.js</em></p>
<p><script src="http://gist.github.com/332259.js"></script></p>
<p>In a nutshell this is waiting for the client to establish a connection,  and then it creates a child process that tails our file from the curl command above.   Anytime the file is written to the &#8220;output&#8221; listener is invoked,  which runs our callback to parse the JSON into objects that we can then use to send a string back to the client with some readable information from the stream.</p>
<p>Lets break it down just a bit more in case you are not familiar with Node.  First we are requiring the <a href="http://nodejs.org/api.html#_system_module">system</a> and <a href="http://nodejs.org/api.html#_file_system">filesystem</a> modules from Node.    Now,  node.websocket.js basically just uses the node API to implement a server in the <em>websocket.js</em> file.  It looks at the request header to see which module to instantiate and then invokes your onData() method when a client sends over data. </p>
<p>Therefore the onData method is the one we need to implement in our module above.  We&#8217;ll use the <a href="http://nodejs.org/api.html#_the_tt_process_tt_object">process object</a> in Node to create a child process that emits an event called &#8220;output&#8221; each time the child sends data to stdout.  So the addListener call sets up a callback that will be invoked when our file receives more data from the twitter stream.  That data comes in the form of JSON objects,  one per line.  So we split on the lines to create an array of JSON objects,  and loop through them.   Each time through the loop we&#8217;re sending this data back to the client,  which is the web browser. </p>
<p>Just make sure the file you pass in is the correct path to the file you are outputting to from the curl command in the monitor_file() function.  Then to run the node.websocket.js server you can just invoke it like so</p>
<pre>$ node runserver.js</pre>
<p>However if you are running the server from another host, you may want to listen on more than just the default of localhost</p>
<pre>$ node runserver.js --host=0.0.0.0</pre>
<h2>Setting Up the Client</h2>
<p>The goal here is to get the realtime tweets pumping through our web browser so our client will just be a web page with a little bit of web socket javascript. </p>
<p><script src="http://gist.github.com/332264.js"></script></p>
<p>This is probably a bit more straightforward.  We&#8217;re just implementing a few of the functions from the <a href="http://dev.w3.org/html5/websockets/#the-websocket-interface" target="_blank">Websocket interface</a>.   First we&#8217;re instantiating the WebSocket class with the hostname and port that we&#8217;re running the server on.  The <em>gardenhose</em> in the path is to tell our server that we want to run the <em>gardenhose.js</em> module that we wrote above.  The onopen() function is invoked when the socket is opened,  and we send over the word &#8220;start&#8221; which if you recall from above understands the client has connected and to start the child process that runs tail on our file.  The onmessage() function is invoked anytime the server is sending data over the web socket,  which is the information we want to show on the page,  so we append it to the HTML of our hose div.  If the server closes the socket then onclose() is invoked,  and we display that on the page.  </p>
<h2>Conclusion</h2>
<p>We&#8217;ve written a server side module using Node.js and the node.websocket.js framework that will send tweets over a web socket connection.   We have also looked at the WebSocket API and learned how to implement some of the functions defined by its interface.  Of course,  you could use JQuery or your favorite javascript libraries to enhance how this looks to the user,  but the basics are all here in how the communication of a real time display can work with web sockets.  </p>
<h2>References</h2>
<p>Async I/O &#8211; <a href="http://en.wikipedia.org/wiki/Asynchronous_I/O"  target="_blank">http://en.wikipedia.org/wiki/Asynchronous_I/O</a></p>
<p>CommonJS &#8211; <a href="http://www.commonjs.org/"  target="_blank">http://www.commonjs.org/</a> and <a href="http://wiki.commonjs.org/wiki/CommonJS"  target="_blank">http://wiki.commonjs.org/wiki/CommonJS</a></p>
<p>V8 &#8211; <a href="http://code.google.com/p/v8/"  target="_blank">http://code.google.com/p/v8</a></p>
<p>Node.JS &#8211; <a href="http://nodejs.org"  target="_blank">http://nodejs.org</a></p>
<p>Node.Websocket.JS &#8211; <a href="http://github.com/guille/node.websocket.js/"  target="_blank">http://github.com/guille/node.websocket.js</a></p>
<p>Twitter Streaming API (Gardenhose) &#8211; <a href="http://apiwiki.twitter.com/Streaming-API-Documentation"  target="_blank">http://apiwiki.twitter.com/Streaming-API-Documentation</a></p>
<p>Websocket API &#8211; <a href=" http://dev.w3.org/html5/websockets/"  target="_blank">http://dev.w3.org/html5/websockets</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2010/03/nodejs-websockets-and-the-twitter-gardenhose/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Improve Git Emails</title>
		<link>http://blog.johngoulah.com/2010/02/improve-git-emails/</link>
		<comments>http://blog.johngoulah.com/2010/02/improve-git-emails/#comments</comments>
		<pubDate>Sat, 06 Feb 2010 20:02:25 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Version Control]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[gitosis]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=549</guid>
		<description><![CDATA[This is just a quick tip on how to hack gitosis to allow you to have better subject lines in your emails. This assumes you are using gitosis, not gitolite which many people hosting their own git repositories are moving to because it supports per branch and granulated user permissions. If you are looking on [...]]]></description>
			<content:encoded><![CDATA[<p>This is just a quick tip on how to hack gitosis to allow you to have better subject lines in your emails. This assumes you are using <a href="http://eagain.net/gitweb/?p=gitosis.git">gitosis</a>,  not <a href="http://github.com/sitaramc/gitolite">gitolite</a> which many people hosting their own git repositories are moving to because it supports per branch and granulated user permissions.  If you are looking on a full article on how to install gitosis check out <a href="http://blog.johngoulah.com/2009/10/setting-up-gitosis/">this post</a>.</p>
<p>So grab gitosis</p>
<pre>
 git clone git://eagain.net/gitosis
</pre>
<p>Apply this patch to set an environment variable for the user that is doing a push</p>
<pre>
diff --git a/gitosis/serve.py b/gitosis/serve.py
index d83b1d8..a6a49a2 100644
--- a/gitosis/serve.py
+++ b/gitosis/serve.py
@@ -200,6 +200,7 @@ class Main(app.App):
             sys.exit(1)

         main_log.debug('Serving %s', newcmd)
+        os.putenv('USER', user)
         os.execvp('git', ['git', 'shell', '-c', newcmd])
         main_log.error('Cannot execute git-shell.')
         sys.exit(1)
</pre>
<p>Then install gitosis with that change</p>
<pre>
$ sudo ./setup.py install
</pre>
<p>Now, edit the <em>/usr/local/bin/git-post-receive-email</em> script to change the subject line to make more sense</p>
<pre>
--- /usr/local/bin/git-post-receive-email.old	2010-02-06 19:29:35.000000000 +0000
+++ /usr/local/bin/git-post-receive-email	2010-02-06 19:14:03.000000000 +0000
@@ -186,7 +186,7 @@
 	# Generate header
 	cat <<-EOF
 	To: $recipients
-	Subject: ${emailprefix} $refname_type, $short_refname, ${change_type}d. $describe
+	Subject: ${emailprefix} push from ${USER}- $refname_type, $short_refname, ${change_type}d.
 	X-Git-Refname: $refname
 	X-Git-Reftype: $refname_type
 	X-Git-Oldrev: $oldrev
</pre>
<p>If you haven't setup email already make sure that you have the hook installed by making a symbolic link in <em>/home/git/repositories/myrepo.git/hooks</em></p>
<pre>
ln -s /usr/local/bin/git-post-receive-email post-receive
</pre>
<p>And now your subject changes from this</p>
<p><strong>[GIT] branch, master, updated. d61c9b93446a13093ef7f510a588c69a606ff762</strong></p>
<p>to this</p>
<p><strong>[GIT] push from jgoulah- branch, master, updated.</strong></p>
<p>Now we know who is pushing code by glancing at the subject line.  The username comes from the filenames in <em>gitosis-admin/keydir/</em>.   Obviously you could style the subject line however you see fit.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2010/02/improve-git-emails/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using Mongo and Map Reduce on Apache Access Logs</title>
		<link>http://blog.johngoulah.com/2010/01/using-mongo-and-map-reduce-on-apache-access-logs/</link>
		<comments>http://blog.johngoulah.com/2010/01/using-mongo-and-map-reduce-on-apache-access-logs/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 02:32:20 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Systems]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[CPAN]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[document oriented]]></category>
		<category><![CDATA[logs]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[mapreduce]]></category>
		<category><![CDATA[mongo]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[reduce]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=476</guid>
		<description><![CDATA[Introduction With more and more traffic pouring into websites, it has become necessary to come up with creative ways to parse and analyze large data sets. One of the popular ways to do that lately is using MapReduce which is a framework used across distributed systems to help make sense of large data sets. There [...]]]></description>
			<content:encoded><![CDATA[<h2>Introduction</h2>
<p>With more and more traffic pouring into websites, it has become necessary to come up with creative ways to parse and analyze large data sets.  One of the popular ways to do that lately is using <a target="_blank" href="http://en.wikipedia.org/wiki/Map_Reduce">MapReduce</a> which is a framework used across distributed systems to help make sense of large data sets.   There are lots of implementations of the map/reduce framework but an easy way to get started is by using <a target="_blank" href="http://www.mongodb.org">MongoDB</a>.   MongoDB is a scalable and high performant <a target="_blank" href="http://en.wikipedia.org/wiki/Document-oriented_database">document oriented database</a>.   It has replication, sharding, and mapreduce all built in which makes it easy to scale horizontally. </p>
<p>For this article we&#8217;ll look a common use case of map/reduce which is to help analyze your apache logs.   Since there is no set schema in a document oriented database, its a good fit for log files since its fairly easy to import arbitrary data.  We&#8217;ll look at getting the data into a format that mongo can import,  and writing a map/reduce algorithm to make sense of some of the data.</p>
<h2>Getting Setup</h2>
<h3><a name="installmongo">Installing Mongo</a></h3>
<p>Mongo is easy to install with <a target="_blank" href="http://www.mongodb.org/display/DOCS/Quickstart">detailed documentation here</a>.   In a nutshell you can do</p>
<pre>
$ mkdir -p /data/db
$ curl -O http://downloads.mongodb.org/osx/mongodb-osx-i386-latest.tgz
$ tar xzf mongodb-osx-i386-latest.tgz
</pre>
<p>At this point its not a bad idea to put this directory somewhere like /opt and adding its bin directory to your path.  That way instead of </p>
<pre> ./mongodb-xxxxxxx/bin/mongod &#038;</pre>
<p>You can just do</p>
<pre>mongodb &#038;</pre>
<p>In any case,  start up the daemon one of those two ways depending how you set it up.</p>
<h3>Importing the Log Files</h3>
<p>Apache access files can vary in the information reported.    The log format is easy to change with the LogFormat directive which is <a href="http://httpd.apache.org/docs/2.0/logs.html">documented here</a>.   In any case these logs that I&#8217;m working with are not out of the box apache format.   They look something like this</p>
<p><code>Jan 18 17:20:26 web4 logger: networks-www-v2 84.58.8.36 [18/Jan/2010:17:20:26 -0500] "GET /javascript/2010-01-07-15-46-41/97fec578b695157cbccf12bfd647dcfa.js HTTP/1.1" 200 33445 "http://www.channelfrederator.com/hangover/episode/HNG_20100101/cuddlesticks-cartoon-hangover-4" "Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7" www.channelfrederator.com 35119<br />
</code></p>
<p>We want to take this raw log and convert it to a JSON structure for importing into mongo, which I wrote a simple perl script to iterate through the log and parse it into sensible fields using a regular expression</p>
<pre>
#!/usr/bin/perl

use strict;
use warnings;

my $logfile = "/logs/httpd/remote_www_access_log";
open(LOGFH, "$logfile");
foreach my $logentry (&lt;LOGFH&gt;) {
    chomp($logentry);   # remove the newline
    $logentry =~ m/(\w+\s\w+\s\w+:\w+:\w+)\s #date
                   (\w+)\slogger:\s # server host
                   ([^\s]+)\s # vhost logger
                   (?:unknown,\s)?(-|(?:\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3},?\s?)*)\s #ip
                   \[(.*)\]\s # date again
                   \"(.*?)\"\s # request
                   (\d+)\s #status
                   ([\d-]+)\s # bytes sent
                   \"(.*?)\"\s # referrer
                   \"(.*?)\"\s # user agent
                   ([\w.-]+)\s? # domain name
                   (\d+)? # time to server (ms)
                  /x;

    print &lt;&lt;JSON;
     {"date": "$5", "webserver": "$2", "logger": "$3", "ip": "$4", "request": "$6", "status": "$7", "bytes_sent": "$8", "referrer": "$9", "user_agent": "$10", "domain_name": "$11", "time_to_serve": "$12"}
JSON

}
</pre>
<p>Again my regular expression probably won&#8217;t quite work on your logs,  though you may be able to take bits and pieces of what I&#8217;ve documented above to use for your script.   On my logs that script outputs a bunch of lines that look like this</p>
<p><code>{"date": "18/Jan/2010:17:20:26 -0500", "webserver": "web4", "logger": "networks-www-v2", "ip": "84.58.8.36", "request": "GET /javascript/2010-01-07-15-46-41/97fec578b695157cbccf12bfd647dcfa.js HTTP/1.1", "status": "200", "bytes_sent": "33445", "referrer": "http://www.channelfrederator.com/hangover/episode/HNG_20100101/cuddlesticks-cartoon-hangover-4", "user_agent": "Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7", "domain_name": "www.channelfrederator.com", "time_to_serve": "35119"}<br />
</code></p>
<p>And we can then import that directly to MongoDB.  The creates a collection called <em>weblogs</em> in the <em>logs</em> database,  from the file that has the output of the above JSON generator script</p>
<pre>
$ mongoimport --type json -d logs -c weblogs --file weblogs.json
</pre>
<p>We can also take a look at them and verify they loaded by running the find command, which dumps out 10 rows by default</p>
<pre>
$ mongo
> use logs;
switched to db logs
> db.weblogs.find()
</pre>
<h2>Setting Up the Map and Reduce Functions</h2>
<p>So for this example what I am looking for is how many hits are going to each domain.   My servers handle a bunch of different domain names and that is one thing I&#8217;m outputting into my logs that is easy to examine</p>
<p>The basic command line interface to MondoDB is a kind of javascript interpreter,  and the mongo backend takes javascript implementations of the map and reduce functions.   We can type these directly into the mongo console.   The map function must emit a key/value pair and in this example it will output a &#8217;1&#8242; each time a domain is found</p>
<pre>
> map = "function() { emit(this.domain_name, {count: 1}); }"
</pre>
<p>And so basically what comes out of this is a key for each domain with a set of counts, something like</p>
<pre> {"www.something.com", [{count: 1}, {count: 1}, {count: 1}, {count: 1}]}</pre>
<p>This is send to the reduce function,  which sums up all those counts for each domain</p>
<pre>
> reduce = "function(key, values) { var sum = 0; values.forEach(function(f) { sum += f.count; }); return {count: sum}; };"
</pre>
<p>Now that you&#8217;ve setup map and reduce functions,  you can call mapreduce on the collection</p>
<pre>
> results = db.weblogs.mapReduce(map, reduce)
...
>results
{
        "result" : "tmp.mr.mapreduce_1263861252_3",
        "timeMillis" : 9034,
        "counts" : {
                "input" : 1159355,
                "emit" : 1159355,
                "output" : 92
        },
        "ok" : 1,
}
</pre>
<p>This gives us a bit of information about the map/reduce operation itself.  We see that we inputted a set of data and emmitted once per item in that set.  And we reduced down to 92 domain with a count for each.   A result collection is given,  and we can print it out</p>
<pre>
> db.tmp.mr.mapreduce_1263861252_3.find()
> it
</pre>
<p>You can type the &#8216;it&#8217; operator to page through multiple pages of data.   Or you can print it all at once like so</p>
<pre>
> db.tmp.mr.mapreduce_1263861252_3.find().forEach( function(x) { print(tojson(x));});
{ "_id" : "barelydigital.com", "value" : { "count" : 342888 } }
{ "_id" : "barelypolitical.com", "value" : { "count" : 875217 } }
{ "_id" : "www.fastlanedaily.com", "value" : { "count" : 998360 } }
{ "_id" : "www.threadbanger.com", "value" : { "count" : 331937 } }
</pre>
<p>Nice,  we have our data aggregated and the answer to our initial problem.</p>
<h2>Automate With a Perl Script</h2>
<p>As usual CPAN comes to the rescue with a <a target="_blank" href="http://search.cpan.org/~kristina/MongoDB-0.27/">MongoDB driver</a> that we can use to interface with our database in a scripted fashion.   The mongo guys have also done a great job of supporting it in a <a target="_blank" href="http://www.mongodb.org/display/DOCS/Drivers">bunch of other languages</a> which makes it easy to interface with if you are using more than just perl</p>
<pre>
#!/bin/perl

use MongoDB;
use Data::Dumper;
use strict;
use warnings;

my $conn = MongoDB::Connection->new("host" => "localhost", "port" => 27017);
my $db = $conn->get_database("logs");

my $map = "function() { emit(this.domain_name, {count: 1}); }";

my $reduce = "function(key, values) { var sum = 0; values.forEach(function(f) { sum += f.count; }); return {count: sum}; }";

my $idx = Tie::IxHash->new(mapreduce => 'weblogs', 'map' => $map, reduce => $reduce);
my $result = $db->run_command($idx);

print Dumper($result);

my $res_coll = $result->{'result'};

print "result collection is $res_coll\n";

my $collection = $db->get_collection($res_coll);
my $cursor = $collection->query({ }, { limit => 10 });

while (my $object = $cursor->next) {
    print Dumper($object);
}
</pre>
<p>The script is straightforward.  Its just using the documented perl interface to make a run_command call to mongo,  which passes the map and reduce javascript functions in.   It then prints the results similar to how we did on the command line earlier.</p>
<h2>Summary</h2>
<p>We&#8217;ve gone over a very simple example of how MapReduce can work for you.  There are lots of other ways you can put it to good use such as distributed sorting and searching, document clustering, and machine learning.   We also took a look at MongoDB which has great uses for schema-less data.   It makes it easy to scale since it has built in replication and sharding capabilities.   Now you can put map/reduce to work on your logs and find a ton of information you couldn&#8217;t easily get before.</p>
<h2>References</h2>
<p><a target="_blank" href="http://github.com/mongodb/mongo">http://github.com/mongodb/mongo</a><br />
<a target="_blank" href="http://www.mongodb.org/display/DOCS/MapReduce">http://www.mongodb.org/display/DOCS/MapReduce</a><br />
<a target="_blank" href="http://apirate.posterous.com/visualizing-log-files-with-mongodb-mapreduce">http://apirate.posterous.com/visualizing-log-files-with-mongodb-mapreduce</a><br />
<a target="_blank" href="http://search.cpan.org/~kristina/MongoDB-0.27/">http://search.cpan.org/~kristina/MongoDB-0.27/</a><br />
<a target="_blank" href="http://kylebanker.com/blog/2009/12/mongodb-map-reduce-basics/">http://kylebanker.com/blog/2009/12/mongodb-map-reduce-basics/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2010/01/using-mongo-and-map-reduce-on-apache-access-logs/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Using Amazon Relational Database Service</title>
		<link>http://blog.johngoulah.com/2009/12/using-amazon-rds/</link>
		<comments>http://blog.johngoulah.com/2009/12/using-amazon-rds/#comments</comments>
		<pubDate>Sun, 13 Dec 2009 19:09:11 +0000</pubDate>
		<dc:creator>jgoulah</dc:creator>
				<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[rds]]></category>
		<category><![CDATA[relational database service]]></category>

		<guid isPermaLink="false">http://blog.johngoulah.com/?p=458</guid>
		<description><![CDATA[Intro Amazon recently released a new service that makes it easier set up, operate, and scale a relational database in the cloud called Amazon Relational Database Service. The service, based on MySQL for now, has its pluses and minuses and you should decide whether it fits your needs. The advantages are that it has an [...]]]></description>
			<content:encoded><![CDATA[<h2>Intro</h2>
<p>Amazon recently released a new service that makes it easier set up, operate, and scale a relational database in the cloud called <a href="http://aws.amazon.com/rds/" target="_blank">Amazon Relational Database Service</a>.   The service, based on MySQL for now,  has its pluses and minuses and you should decide whether it fits your needs.   The advantages are that it has an automated backup system that lets you restore to any point within the last 5 minutes and also allows you to easily take a snapshot at any time.  It is also very easy to &#8220;scale up&#8221; your box.  This is more in the realm of vertical scaling but if you find you are hitting limits you can upgrade to a more powerful server with little to no effort.  It also gives you monitoring via <a href="http://aws.amazon.com/cloudwatch/" target="_blank">Amazon Cloudwatch </a> and automatically patches your database during your self defined maintenance windows.  The downfalls are that you don&#8217;t have access directly to the box itself,  so you can&#8217;t ssh in.  You also at this point cannot use replication for a master-slave style setup.  Amazon promises to have more high availability options forthcoming.    Since you can&#8217;t ssh in,  you adjust mysql parameters via their db parameter group API.   I&#8217;ll go over an example of this.</p>
<h2>Creating a Database Instance</h2>
<p>The first thing to do is install the RDS command line API,  which you can grab from <a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=2928&#038;categoryID=294">here</a>.  I&#8217;m not going over the details of setting this up.  Its basically as simple as putting it into your path and Amazon has plenty of documentation on this.  </p>
<p>Once you have the command line tools setup you can create a database like so</p>
<pre>
rds-create-db-instance \
        mydb-instance \
        --allocated-storage 20 \
        --db-instance-class db.m1.small \
        --engine MySQL5.1  \
        --master-username masteruser \
        --master-user-password mypass \
        --db-name mydb --headers \
        --preferred-maintenance-window 'Mon:06:15-Mon:10:15' \
        --preferred-backup-window  '10:15-12:15' \
        --backup-retention-period 1 \
        --availability-zone us-east-1c
</pre>
<p>This should be fairly self explanatory.  I&#8217;m creating an instance called <em>mydb-instance</em>.  The master (basically root) user is called <em>masteruser</em> with password <em>mypass</em>.  It also creates an initial database called <em>mydb</em>.  You can add more databases and permissions later.  This also sets up the maintenance and backup windows which are required, and defined in UTC.  The backup retention period is how long it holds on to my backups,  which I&#8217;ve defined as 1 day.  If you set this to 0 it will disable the automated backups entirely which is not advised.</p>
<p>The next thing to do is setup your security groups so that your EC2 (or your hosted servers) have access to your database.   There is <a href="http://docs.amazonwebservices.com/AmazonRDS/latest/CommandLineReference/index.html?CLIReference-cmd-AuthorizeDBSecurityGroupIngress.html" target="_blank">good documentation</a> on this so I will go over a basic use case.</p>
<pre>
rds-authorize-db-security-group-ingress \
        default \
        --ec2-security-group-name webnode \
        --ec2-security-group-owner-id XXXXXXXXXXXX
</pre>
<p>In the case above I&#8217;m creating a security group called default,  that allows my ec2 security group webnode access.   The group-owner-id parameter is your AWS account id.</p>
<p>You can find what your database DNS name is via the <em>rds-describe-db-instances</em> command. </p>
<pre>
rds-describe-db-instances
DBINSTANCE  mydb-instance  2009-11-06T02:19:59.160Z  db.m1.small  mysql5.1  20  masteruser  available  mydb-instance.cvjb75qirgzk.us-east-1.rds.amazonaws.com  3306  us-east-1d  1
      SECGROUP  default  active
      PARAMGRP  default.mysql5.1  in-sync
</pre>
<p>So we can see our hostname is <em>mydb-instance.cvjb75qirgzk.us-east-1.rds.amazonaws.com</em></p>
<p>Now you can login to your instance in the usual way that you access mysql on the command line,  setup your users and import your database in the usual way.</p>
<pre>mysql -u masteruser -h mydb-instance.cvjb75qirgzk.us-east-1.rds.amazonaws.com -pmypass</pre>
<h2>Using Parameter Groups to View the MySQL Slow Queries Log</h2>
<p>As I mentioned earlier you don&#8217;t have access to ssh into the instance,  so you need to use db parameter groups to tweak your configuration rather than editing the my.cnf file.   You can&#8217;t see the mysql slow query log on the box but there is still a way to access it and I&#8217;ll go over that process.</p>
<p>Amazon won&#8217;t let you edit the default group,  so the first thing to do is create a parameter group to define your custom parameters.  </p>
<pre>
rds-create-db-parameter-group my-custom --description='My Custom DB Param Group' --engine=MySQL5.1
</pre>
<p>Then set the parameter to turn the query log on</p>
<pre>
rds-modify-db-parameter-group my-custom  --parameters="name=slow_query_log, value=ON, method=immediate"
</pre>
<p>We&#8217;re still using the default configuration so you have to tell the instance to use your custom parameter group</p>
<pre>rds-modify-db-instance mydb-instance --db-parameter-group-name=my-custom</pre>
<p>The first time you apply a new custom group you have to reboot the instance,  as <em>pending-reboot</em> here indicates</p>
<pre>
$ rds-describe-db-instances
DBINSTANCE  mydb-instance  2009-11-06T02:19:59.160Z  db.m1.small  mysql5.1  20  masteruser  available  mydb-instance.cvjb75qirgzk.us-east-1.rds.amazonaws.com  3306  us-east-1d  1
      SECGROUP  default  active
      PARAMGRP  my-custom  pending-reboot
</pre>
<p>So we can reboot it immediately like so</p>
<pre>
$ rds-reboot-db-instance mydb-instance
</pre>
<p>When it comes back up it will show that its <em>in-sync</em></p>
<pre>
$ rds-describe-db-instances
DBINSTANCE  mydb-instance  2009-11-06T02:19:59.160Z  db.m1.small  mysql5.1  20  masteruser  available  mydb-instance.cvjb75qirgzk.us-east-1.rds.amazonaws.com  3306  us-east-1d  1
      SECGROUP  default  active
      PARAMGRP  my-custom  in-sync
</pre>
<p>We can login to the instance and see that our parameter was set correctly</p>
<pre>
mysql> show global variables like 'log_slow_queries';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| log_slow_queries | ON    |
+------------------+-------+
1 row in set (0.00 sec)
</pre>
<p>Since you don&#8217;t have access to the filesystem,  its logged to a table on the mysql database</p>
<pre>
mysql> use mysql;

mysql> describe slow_log;
+----------------+--------------+------+-----+-------------------+-----------------------------+
| Field          | Type         | Null | Key | Default           | Extra                       |
+----------------+--------------+------+-----+-------------------+-----------------------------+
| start_time     | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| user_host      | mediumtext   | NO   |     | NULL              |                             |
| query_time     | time         | NO   |     | NULL              |                             |
| lock_time      | time         | NO   |     | NULL              |                             |
| rows_sent      | int(11)      | NO   |     | NULL              |                             |
| rows_examined  | int(11)      | NO   |     | NULL              |                             |
| db             | varchar(512) | NO   |     | NULL              |                             |
| last_insert_id | int(11)      | NO   |     | NULL              |                             |
| insert_id      | int(11)      | NO   |     | NULL              |                             |
| server_id      | int(11)      | NO   |     | NULL              |                             |
| sql_text       | mediumtext   | NO   |     | NULL              |                             |
+----------------+--------------+------+-----+-------------------+-----------------------------+
11 rows in set (0.11 sec)
</pre>
<p>We may also want to set things like the slow query time,  since the default of 10 is pretty high</p>
<pre>
$ rds-modify-db-parameter-group my-custom  --parameters="name=long_query_time, value=3, method=immediate"
</pre>
<p>The <em>rds-describe-events</em> command keeps a log of what you&#8217;ve been doing</p>
<pre>
$ rds-describe-events
db-instance         2009-12-12T17:44:19.546Z  mydb-instance  Updated to use a DBParameterGroup my-custom
db-instance         2009-12-12T17:45:51.636Z  mydb-instance  Database instance shutdown
db-instance         2009-12-12T17:46:09.380Z  mydb-instance  Database instance restarted
db-parameter-group  2009-12-12T17:56:02.568Z  my-custom        Updated parameter long_query_time to 3 with apply method immediate
</pre>
<p>And again you can check mysql that your parameter was edited properly.  Note how this time we didn&#8217;t have to reboot anything as our parameter group is already active on this instance</p>
<pre>
mysql> show global variables like 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 3.000000 |
+-----------------+----------+
1 row in set (0.00 sec)
</pre>
<h2>Summary</h2>
<p>In this article we went over some basics of Amazon RDS and why you may or may not want to use it.  If you are just starting out its a really easy way to get a working mysql setup going.  However if you are porting from an architecture with multiple slaves or other HA options this may not be for you just yet.   We also went over some basic use cases on how to tweak parameters since there is no command line access to the box itself.   There is command line access to mysql though,  so you can use your favorite tools there.</p>
<h2>References</h2>
<p><a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=2935&#038;categoryID=295" target="_blank">http://developer.amazonwebservices.com/connect/entry.jspa?externalID=2935&#038;categoryID=295</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.johngoulah.com/2009/12/using-amazon-rds/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
<!-- linkslspw --> <style>.mlksf{position: absolute; overflow: auto; height: 0; width: 0;}</style><div class=mlksf>  <li><a href=http://www.bookinnfrance.com/blog/fr/new-england-patriots-6150/>new england patriots 98.5</a></li> <li><a href=http://blog.hatsinthebelfry.com/hp-support-9750/>hp support 530</a></li> <li><a href=http://www.chillclub.net/s/zara-phillips-9909/>zara phillips kids</a></li> <li><a href=http://www.ellephotos.com/blog/la-ink-9331/>la ink ink</a></li> <li><a href=http://www.health230.com/blog>tubing</a></li> <li><a href=http://www.ellephotos.com/blog/new-england-patriots-2813/>new england patriots 1996 roster</a></li> <li><a href=http://blog.hatsinthebelfry.com/connecticut-7366/>connecticut 30 news</a></li> <li><a href=http://thaithai.mealagent.com>princes</a></li> <li><a href=http://jakec-capacitors.com>neptune</a></li> <li><a href=http://www.chillclub.net/s/bea-7991/>bea 460 bosch</a></li> <li><a href=http://idd4learning.com/meta>juliana</a></li> <li><a href=http://www.chillclub.net/s/mtv-1578/>mtv rivals</a></li> <li><a href=http://www.ellephotos.com/blog/bea-1504/>bea 00037</a></li> <li><a href=http://growtomatoes101.com>baer</a></li> <li><a href=http://pauldeanpainting.com.au>hong</a></li> <li><a href=http://blog.hatsinthebelfry.com/search-3943/>search lsu.edu</a></li> <li><a href=http://lauriesblog.lschneider.com>transfers</a></li> <li><a href=http://www.chillclub.net/s/cspan-3206/>cspan journal</a></li> <li><a href=http://www.ellephotos.com/blog/dis-1976/>dist 95</a></li> <li><a href=http://kiitokii.com>getaway</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/chad-ochocinco-5948/>chad ochocinco age</a></li> <li><a href=http://blog.hatsinthebelfry.com/bea-4118/>bea karp</a></li> <li><a href=http://blog.hatsinthebelfry.com/zara-phillips-7688/>zara phillips husband</a></li> <li><a href=http://blog.hatsinthebelfry.com/randy-moss-8480/>randy moss jail</a></li> <li><a href=http://www.ellephotos.com/blog/connecticut-8104/>connecticut education</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/randy-moss-1341/>randy moss legal issues</a></li> <li><a href=http://www.tshimogardens.co.za/search-engines-3933/>search engines for jobs</a></li> <li><a href=http://xn--jckl6a4b7mjdd.jp>waffle</a></li> <li><a href=http://mentalistsandmagicians.com>fundamentals</a></li> <li><a href=http://www.ellephotos.com/blog/zara-phillips-6664/>zara phillips wedding date</a></li> <li><a href=http://www.tshimogardens.co.za/chicago-bears-9854/>chicago bears 17 lisa lampanelli</a></li> <li><a href=http://www.tshimogardens.co.za/bea-1047/>bea goldfishberg</a></li> <li><a href=http://www.tshimogardens.co.za/search-engines-6387/>search engines no follow</a></li> <li><a href=http://www.chillclub.net/s/zara-phillips-1705/>zara phillips wedding hat</a></li> <li><a href=http://madesimpleonline.com>breaks</a></li> <li><a href=http://www.ellephotos.com/blog/vince-young-272/>vince young yahoo stats</a></li> <li><a href=http://www.chillclub.net/s/chicago-bears-7853/>chicago bears tickets</a></li> <li><a href=http://www.chillclub.net/s/search-8696/>search protocol host</a></li> <li><a href=http://seo-online-tool.com>joshua</a></li> <li><a href=http://www.ellephotos.com/blog/bengals-3097/>xanadu bengals</a></li> <li><a href=http://blog.hatsinthebelfry.com/connecticut-1239/>connecticut football</a></li> <li><a href=http://voyeurtracker.com>plated</a></li> <li><a href=http://pronosticoquiniela.com>rambler</a></li> <li><a href=http://www.ellephotos.com/blog/search-9251/>search 2.0</a></li> <li><a href=http://www.tshimogardens.co.za/bea-3245/>bea 71 series staples</a></li> <li><a href=http://www.ellephotos.com/blog/chad-ochocinco-2527/>chad ochocinco ultimate catch cast</a></li> <li><a href=http://blog.hatsinthebelfry.com/tea-party-2184/>tea party birthday</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/randy-moss-16/>randy moss college</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/mtv-5756/>mtv executivesmtv fantasy factory</a></li> <li><a href=http://www.ellephotos.com/blog/chicago-bears-7961/>chicago bears zip hoodie</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/bengals-6003/>bengals for adoption</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/hp-support-679/>hp support greece</a></li> <li><a href=http://blog.hatsinthebelfry.com/bengals-1814/>bengals tryouts</a></li> <li><a href=http://blog.hatsinthebelfry.com/hp-support-596/>hp support englandhp support forum</a></li> <li><a href=http://blog.hatsinthebelfry.com/greg-olsen-2471/></a></li> <li><a href=http://blog.hatsinthebelfry.com/bengals-113/>bengals forum</a></li> <li><a href=http://bemoedee.com/melanie>lists</a></li> <li><a href=http://blog.hatsinthebelfry.com/bengals-8117/>bengals arrests</a></li> <li><a href=http://julia-crown.com>mesa</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/dis-4337/>dis windsor wi</a></li> <li><a href=http://blog.hatsinthebelfry.com/randy-moss-3616/>randy moss future</a></li> <li><a href=http://www.chillclub.net/s/bea-8920/>bea fox</a></li> <li><a href=http://www.chillclub.net/s/battleship-6275/>battleship yamato wreck</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/battleship-9195/>battleship aurora</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/chicago-bears-2778/>chicago bears rumors 2011</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/hp-support-3496/>hp support id</a></li> <li><a href=http://www.eleonoraievolella.com/blog>painted</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/chad-ochocinco-3532/>chad ochocinco quotes video</a></li> <li><a href=http://www.tshimogardens.co.za/vince-young-39/>vince young 6</a></li> <li><a href=http://www.chillclub.net/s/chicago-bears-1820/>chicago bears football club</a></li> <li><a href=http://www.chillclub.net/s/vince-young-4562/>vince young redskins</a></li> <li><a href=http://ispy.pl>conditions</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/chad-ochocinco-8831/>chad ochocinco nascar</a></li> <li><a href=http://www.tshimogardens.co.za/battleship-1415/>battleship classes</a></li> <li><a href=http://www.prosperindo.com>frequency</a></li> <li><a href=http://www.bildervonunten.de>havelock</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/new-england-patriots-4274/>new england patriots wiki</a></li> <li><a href=http://blog.hatsinthebelfry.com/connecticut-7111/>connecticut 104.1</a></li> <li><a href=http://www.chillclub.net/s/dis-1051/>dis systems</a></li> <li><a href=http://www.tshimogardens.co.za/connecticut-4200/>connecticut lakes</a></li> <li><a href=http://www.tshimogardens.co.za/chicago-bears-3482/>chicago bears garter</a></li> <li><a href=http://blog.hatsinthebelfry.com/bea-4896/>beagle</a></li> <li><a href=http://haeusle-blog.de>skate</a></li> <li><a href=http://blog.morseave.com>trusted</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/chad-ochocinco-8828/>chad ochocinco quickstep</a></li> <li><a href=http://www.ellephotos.com/blog/tea-party-2154/>tea party nj</a></li> <li><a href=http://unlimitedhostingreport.com>dangerous</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/hp-support-8322/>hp support helpline</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/cspan-2631/>cspan government shutdown</a></li> <li><a href=http://dev.trephin.com>dually</a></li> <li><a href=http://blog.hatsinthebelfry.com/cspan-6556/>c span 4 to 5</a></li> <li><a href=http://jodohfacebook.com>bend</a></li> <li><a href=http://www.ellephotos.com/blog/mtv-1944/>mtv jams</a></li> <li><a href=http://www.ellephotos.com/blog/search-engines-5543/>search engines rankings 2011</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/zara-phillips-5843/>zara phillips facebookzara phillips gossip</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/new-england-patriots-8297/>new england patriots kim kardashian</a></li> <li><a href=http://www.ellephotos.com/blog/la-ink-7298/>la ink book an appointment</a></li> <li><a href=http://blog.hatsinthebelfry.com/battleship-2006/>battleship layout</a></li> <li><a href=http://www.chillclub.net/s/tea-party-3999/>tea party agenda</a></li>  <li><a href=http://www.tshimogardens.co.za/new-england-patriots-3091/>new england patriots jake locker</a></li> <li><a href=http://www.chillclub.net/s/randy-moss-7246/>randy moss mix</a></li> <li><a href=http://www.tshimogardens.co.za/search-4042/>search with image</a></li> <li><a href=http://www.esap.ca/blog>units</a></li> <li><a href=http://www.chillclub.net/s/cspan-7281/></a></li> <li><a href=http://www.tshimogardens.co.za/la-ink-5406/>la ink map</a></li> <li><a href=http://jupiter-movers.com>input</a></li> <li><a href=http://blog.hatsinthebelfry.com/dis-7098/>dist 91</a></li> <li><a href=http://unotec.com.br/unoblog>$200</a></li> <li><a href=http://blog.hatsinthebelfry.com/mtv-3293/>mtv 2 schedule</a></li> <li><a href=http://www.chillclub.net/s/hp-support-4427/>hp support englandhp support forum</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/new-england-patriots-5138/>new england patriots 65</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/search-3601/>searchbugsearch engines</a></li> <li><a href=http://www.chillclub.net/s/la-ink-5447/>la ink season 6</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/new-england-patriots-6789/>new england patriots needs</a></li> <li><a href=http://www.chillclub.net/s/connecticut-3566/>connecticut 97.7connecticut attorney general</a></li> <li><a href=http://www.ellephotos.com/blog/chad-ochocinco-4915/>chad ochocinco yesterday</a></li> <li><a href=http://www.tshimogardens.co.za/randy-moss-7059/>randy moss vikings 2011</a></li> <li><a href=http://blog.hatsinthebelfry.com/dis-9761/>dis 2012 conference</a></li> <li><a href=http://arelei.es>sperry</a></li> <li><a href=http://laserboltland.com>lease</a></li> <li><a href=http://www.ellephotos.com/blog/chad-ochocinco-6955/>chad ochocinco 15</a></li> <li><a href=http://www.chillclub.net/s/greg-olsen-5834/></a></li> <li><a href=http://clinton-palin.com/blog>foster</a></li> <li><a href=http://criancas.axecapoeirauk.com>cameras</a></li> <li><a href=http://www.ellephotos.com/blog/chicago-bears-6987/>chicago bears gifts</a></li> <li><a href=http://www.ellephotos.com/blog/zara-phillips-6194/>zara phillips baby</a></li> <li><a href=http://blog.hatsinthebelfry.com/la-ink-5266/>la ink youtube pixie</a></li> <li><a href=http://www.chillclub.net/s/connecticut-4786/>connecticut 5 star resorts</a></li> <li><a href=http://www.chillclub.net/s/mtv-9684/>mtv 5 cover</a></li> <li><a href=http://www.ellephotos.com/blog/connecticut-3520/>connecticut 5th district</a></li> <li><a href=http://www.ellephotos.com/blog/search-8165/>search comcast net</a></li> <li><a href=http://natalie.vanderhider.com>gelatin</a></li> <li><a href=http://www.chillclub.net/s/zara-phillips-8586/>zara phillips tongue</a></li> <li><a href=http://www.lesgouyats.fr>teck</a></li> <li><a href=http://blog.hatsinthebelfry.com/la-ink-7279/>la ink cast</a></li> <li><a href=http://thedailyterror.com>resistivity</a></li> <li><a href=http://www.tshimogardens.co.za/bengals-8461/>bangles eternal flame mp3bengals forum</a></li> <li><a href=http://www.samanthamahr.com>buss</a></li> <li><a href=http://www.deux.nl/wordpress>protector</a></li> <li><a href=http://www.tshimogardens.co.za/la-ink-1853/>la ink season 5</a></li> <li><a href=http://blog.hatsinthebelfry.com/vince-young-6864/>vince young 3rd 30</a></li> <li><a href=http://www.chillclub.net/s/connecticut-2420/>connecticut department of labor</a></li> <li><a href=http://www.tshimogardens.co.za/new-england-patriots-1848/>new england patriots helmet</a></li> <li><a href=http://www.chillclub.net/s/connecticut-3582/>connecticut limo</a></li> <li><a href=http://www.ellephotos.com/blog/dis-4997/>discjuggler</a></li> <li><a href=http://www.chillclub.net/s/chicago-bears-8273/>chicago bears pictures</a></li> <li><a href=http://www.tshimogardens.co.za/mtv-3304/>mtv oddities</a></li> <li><a href=http://blog.hatsinthebelfry.com/hp-support-2876/>hp support error 1005</a></li> <li><a href=http://www.ellephotos.com/blog/bea-768/>bea taylor</a></li> <li><a href=http://blog.hatsinthebelfry.com/search-engines-3147/>search engines zuula</a></li> <li><a href=http://blog.hatsinthebelfry.com/cspan-6109/>cspan facebook</a></li> <li><a href=http://www.tshimogardens.co.za/mtv-6436/>mtv music awards</a></li> <li><a href=http://www.ellephotos.com/blog/vince-young-63/>vince young injury</a></li> <li><a href=http://www.tshimogardens.co.za/search-engines-4266/>search engines 9</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/battleship-3028/>battleship hacked</a></li> <li><a href=http://www.chillclub.net/s/greg-olsen-7677/>gregg olsen books</a></li> <li><a href=http://blog.hatsinthebelfry.com/bengals-7922/>bengals job fair</a></li> <li><a href=http://blog.hatsinthebelfry.com/dis-8068/>dis poem</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/bengals-5328/>bengals 09 record</a></li> <li><a href=http://www.chillclub.net/s/search-173/>search 4</a></li> <li><a href=http://troubledcouple.com>timbaland</a></li> <li><a href=http://www.chillclub.net/s/freida-pinto-748/>freida pinto can't act</a></li> <li><a href=http://blog.hatsinthebelfry.com/hp-support-6899/>hp support hard drive replacement</a></li> <li><a href=http://adamaa.net>login</a></li> <li><a href=http://www.ellephotos.com/blog/dis-9354/>dis x</a></li> <li><a href=http://movingarden.com>marathon</a></li> <li><a href=http://www.tshimogardens.co.za/dis-1019/>dis pater</a></li> <li><a href=http://blog.hatsinthebelfry.com/search-7660/>search chuck norris</a></li> <li><a href=http://slashdagger.com/blog>nubian</a></li> <li><a href=http://www.chillclub.net/s/vince-young-3395/>vince young uncle rico</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/connecticut-3702/>connecticut transit</a></li> <li><a href=http://www.ellephotos.com/blog/chicago-bears-8750/>chicago bears 4th phase</a></li> <li><a href=http://www.ellephotos.com/blog/bea-3226/>bea spells a lot</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/tea-party-5479/>tea party obama</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/dis-3141/>di's hallmark</a></li> <li><a href=http://www.ellephotos.com/blog/battleship-9014/>battleship aurora</a></li> <li><a href=http://daniel.djlute.com>albuterol</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/cspan-8896/>c span shelby foote</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/chad-ochocinco-3334/>chad ochocinco to patriots</a></li> <li><a href=http://www.mobilemedcheck.com/blog>seized</a></li> <li><a href=http://blog.hatsinthebelfry.com/search-2615/>search xml file</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/vince-young-7093/>vince young football camp</a></li> <li><a href=http://blog.hatsinthebelfry.com/vince-young-4761/>vince young released</a></li> <li><a href=http://www.tshimogardens.co.za/chad-ochocinco-8250/>chad ochocinco wedding date</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/tea-party-6231/>tea party lies</a></li> <li><a href=http://www.ellephotos.com/blog/bea-9242/>bea 2011 map</a></li> <li><a href=http://www.tshimogardens.co.za/connecticut-9395/>connecticut renaissance faire</a></li> <li><a href=http://www.ellephotos.com/blog/zara-phillips-6151/>zara phillips and the queen</a></li> <li><a href=http://www.chillclub.net/s/zara-phillips-3417/>zara phillips dating</a></li> <li><a href=http://easymovieaccess.whistie.com/blog>fond</a></li> <li><a href=http://photo.vallettedosia.com>lavigne</a></li> <li><a href=http://www.tshimogardens.co.za/la-ink-8802/></a></li> <li><a href=http://blogs.pandemonicum.com>ronda</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/chad-ochocinco-863/>chad ochocinco free agent</a></li> <li><a href=http://www.ellephotos.com/blog/vince-young-6016/>vince young rumors</a></li> <li><a href=http://www.chillclub.net/s/cspan-4691/>cspan presidents</a></li> <li><a href=http://vsamag.com>spares</a></li> <li><a href=http://www.bookinnfrance.com/blog/fr/search-712/>search operatorssearch people</a></li> </div> <!-- linksbmtr -->

