<?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>Brain Lint</title>
	<atom:link href="http://www.monkeyatlarge.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.monkeyatlarge.com</link>
	<description>Random musings on life, technology and other miscellany.</description>
	<lastBuildDate>Fri, 03 Feb 2012 21:34:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Another use for generate_series: row multiplier</title>
		<link>http://www.monkeyatlarge.com/archives/2012/02/03/another-use-for-generate_series-row-multiplier/</link>
		<comments>http://www.monkeyatlarge.com/archives/2012/02/03/another-use-for-generate_series-row-multiplier/#comments</comments>
		<pubDate>Fri, 03 Feb 2012 21:33:26 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Data]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=442</guid>
		<description><![CDATA[I had a request the other day: how many simultaneous users are on the site, by time of day. I already have a session database that&#8217;s computed nightly from weblogs: it contains the times at which each session started and ended. I thought for sure the next step would be to dump some data, then [...]]]></description>
			<content:encoded><![CDATA[<p>I had a request the other day: how many simultaneous users are on the site, by time of day. I already have a session database that&#8217;s computed nightly from weblogs: it contains the times at which each session started and ended. </p>
<pre class="brush: sql; title: ; notranslate">
CREATE TABLE sessions
(
  user_id integer NOT NULL,
  start_at timestamp without time zone,
  end_at timestamp without time zone,
  duration double precision,
  views integer
)
</pre>
<p>I thought for sure the next step would be to dump some data, then write some Ruby or R to scan through sessions and see how many sessions were open at a time.</p>
<p>Until I came up with a nice solution in SQL (Postgres). Stepping back, if I can sample from sessions at say, one-minute intervals, I can count the number of distinct sessions open at each minute. What I need is a row per session per minute spanned. <a href="http://www.postgresql.org/docs/9.1/static/functions-srf.html">Generate_series</a> is a &#8220;set returning function&#8221; that can do just that. In the snippet below, I use generate_series to generate a set of (whole) minutes from the start of the session to the end of the session. That essentially multiplies the session row into n rows, one for each of the minutes the session spans. </p>
<p>From there, it&#8217;s easy to do a straight forward group by, counting distinct user_id:</p>
<pre class="brush: sql; title: ; notranslate">
with rounded_sessions as (
select user_id, start_at, end_at,
generate_series(date_trunc('minute',start_at), end_at, '1 minute') to_the_minute from sessions
where start_at between '2012-01-21' and '2012-01-28'
)
select to_the_minute, count(distinct user_id) from rounded_sessions group by 1
</pre>
<p>The date_trunc call is important so that session rows are aligned to whole minutes, if that&#8217;s not done, then none of the rows will align for the counts. </p>
<p>That set won&#8217;t include rows that had no users logged in.  To do that, the query below will use generate_series again to generate all the minutes from the first minute present to the last, then left join the counts to that set, coalescing missing entries to zero.</p>
<pre class="brush: sql; title: ; notranslate">

with rounded_sessions as (
select plm_users.user_id, start_at, end_at,
generate_series(date_trunc('minute',start_at), end_at, '1 minute') as to_the_minute
from sessions
where start_at between '2012-01-21' and '2012-01-28'
),
counts_by_minute as (
select to_the_minute, count(distinct user_id) from rounded_sessions
group by 1
),
all_the_minutes as (
select generate_series(min(to_the_minute), max(to_the_minute), '1 minute') as minute_fu from rounded_sessions
)

select to_the_minute , coalesce(count, 0) as users from all_the_minutes
left join counts_by_minute on all_the_minutes.minute_fu = counts_by_minute.to_the_minute
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2012/02/03/another-use-for-generate_series-row-multiplier/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Computing Distinct Items Across Sliding Windows in SQL</title>
		<link>http://www.monkeyatlarge.com/archives/2012/02/03/computing-distinct-items-across-sliding-windows-in-sql/</link>
		<comments>http://www.monkeyatlarge.com/archives/2012/02/03/computing-distinct-items-across-sliding-windows-in-sql/#comments</comments>
		<pubDate>Fri, 03 Feb 2012 21:05:40 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Data]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=425</guid>
		<description><![CDATA[As a member of PatientsLikeMe&#8216;s Data team, from time to time we&#8217;re asked to compute how many unique users did action X on the site within a date range, say 28 days, or several date ranges (1,14,28 days for example). It&#8217;s easy enough to do that for a given day, but to do that for [...]]]></description>
			<content:encoded><![CDATA[<p>As a member of <a href="http://www.patientslikeme.com">PatientsLikeMe</a>&#8216;s Data team, from time to time we&#8217;re asked to compute how many unique users did action X on the site within a date range, say 28 days, or several date ranges (1,14,28 days for example). It&#8217;s easy enough to do that for a given day, but to do that for every day over a span of time (in one query) took some thinking. Here&#8217;s what I came up with.</p>
<p><strong>One day at a time</strong></p>
<p>First, a simplified example table:</p>
<pre class="brush: sql; title: ; notranslate">
create table events (
  user_id integer,
  event varchar,
  date date
)
</pre>
<p>Getting unique user counts by event on any given day is easy. Below, we&#8217;ll get the counts of unique users by events for the 7 days leading up to Valentine&#8217;s day:</p>
<pre class="brush: sql; title: ; notranslate">
select count(distinct user_id), event from events
where date between '2011-02-07' and '2011-02-14'
group by 2
</pre>
<p><strong>Now Do That For Every Day</strong></p>
<p>The simplest thing that could possibly work is to just issue that query to compute the stats for the time span desired. We&#8217;re looking for something faster, and a bit more elegant.</p>
<p>Stepping back a bit, for a seven day time window, we&#8217;re asking that an event on 2/7/2011 count for that day, and also count for the 6 following days &#8211; effectively we&#8217;re mapping the events of each day onto itself and 6 other days. That sounds like a SQL join waiting to happen. Once the join happens, its easy to group by the mapped date, and do a distinct count.</p>
<p>With a table like the one below</p>
<table>
<thead>
<tr>
<th>from_date</th>
<th>to_date</th>
</tr>
</thead>
<tbody>
<tr>
<td>2011-01-01</td>
<td>2011-01-01</td>
</tr>
<tr>
<td>2011-01-01</td>
<td>2011-01-02</td>
</tr>
<tr>
<td>2011-01-01</td>
<td>2011-01-03</td>
</tr>
<tr>
<td>2011-01-01</td>
<td>2011-01-04</td>
</tr>
<tr>
<td>2011-01-01</td>
<td>2011-01-05</td>
</tr>
<tr>
<td>2011-01-01</td>
<td>2011-01-06</td>
</tr>
<tr>
<td>2011-01-01</td>
<td>2011-01-07</td>
</tr>
<tr>
<td>2011-01-02</td>
<td>2011-01-02</td>
</tr>
<tr>
<td colspan='2'>&#8230;</td>
</tr>
</tbody>
</table>
<p>This SQL becomes easy.</p>
<pre class="brush: sql; title: ; notranslate">
select to_date, event, count(distinct user_id) from events
join dates_plus_7 on events.date = dates_plus_7.from_date
group by 1,2
</pre>
<table>
<thead>
<tr>
<th>to_date</th>
<th>event</th>
<th>count</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan='3'>&#8230;</td>
</tr>
<tr>
<td>2011-01-05</td>
<td>bar</td>
<td>20</td>
</tr>
<tr>
<td>2011-01-05</td>
<td>baz</td>
<td>27</td>
</tr>
<tr>
<td>2011-01-05</td>
<td>foo</td>
<td>24</td>
</tr>
<tr>
<td>2011-01-06</td>
<td>bar</td>
<td>31</td>
</tr>
<tr>
<td colspan='3'>&#8230;</td>
</tr>
</tbody>
</table>
<p>You&#8217;ll then need to trim the ends of your data to adjust for where the windows ran off the edge of the data.<br />
That works for me on Postgresql 8.4. Your mileage may vary with other brands.</p>
<p><strong>How Do I Get One of Those?</strong><br />
A dates table like that is a one-liner using the generate_series method:</p>
<pre class="brush: sql; title: ; notranslate">
select date::date as from_date, date::date+plus_day as to_date from
 generate_series('2011-01-01'::date, '2011-02-28'::date, '1 day') as date,
 generate_series(0,6,1) as plus_day ;
</pre>
<p>There we get the cartesian product of the set of dates in the desired range, and the set of numbers from 0 to 6. Sum the two, treating the numbers as offsets and you&#8217;re done.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2012/02/03/computing-distinct-items-across-sliding-windows-in-sql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Archipelago Of Accounts &#8211; The Banks Always Win</title>
		<link>http://www.monkeyatlarge.com/archives/2011/08/08/archipelago-of-accounts-the-banks-always-win/</link>
		<comments>http://www.monkeyatlarge.com/archives/2011/08/08/archipelago-of-accounts-the-banks-always-win/#comments</comments>
		<pubDate>Mon, 08 Aug 2011 11:58:46 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[Politics]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=436</guid>
		<description><![CDATA[At work, our health insurance has been switched to a high-deductible PPO. Not to worry, we&#8217;ve also been granted Health Savings Accounts (HSA) in which to save money, tax-free, to pay bills before meeting the deductible. That&#8217;s all well and good, but I can&#8217;t shake the feeling every time legislation comes out to do some [...]]]></description>
			<content:encoded><![CDATA[<p>At work, our health insurance has been switched to a high-deductible PPO. Not to worry, we&#8217;ve also been granted Health Savings Accounts (HSA) in which to save money, tax-free, to pay bills before meeting the deductible.</p>
<p>That&#8217;s all well and good, but I can&#8217;t shake the feeling every time legislation comes out to do some activity (retire, save for education, health care) the <strong>only winner is the financial services industry.</strong></p>
<p>Here&#8217;s why: all of these activities requires one to maroon a slice of money into an account designated for that purpose. What comes with accounts? That&#8217;s right, fees to the bank. The Wells-Fargo HSA we&#8217;ve got is $4.25 a month (paid, for now, by work). That&#8217;s $51 a year to hold money. The interest rate is a paltry 0.1%, so with $2000 in that account (the minimum cash balance before we&#8217;re allowed to invest), I&#8217;d make about $2.00, (net -$49 if I was paying the fees, as I will one day) Thanks for nothing. Further, while some banks graciously waive fees for meeting minimum balances, it&#8217;s harder for many people to meet the balance since their money is split so many ways.</p>
<p>These accounts limit my flexibility to spend as life events occur, limit the returns on my money, and cost me fees, and headaches. More statements to read, cards to carry, and fine print to decode. </p>
<p>If costs are to be tax-deductible, why not fix the tax code instead, so that all medical expenses, instead of those over a certain amount, are tax deductible, instead of these shameless handouts to the banks? Let me deduct things come tax time. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2011/08/08/archipelago-of-accounts-the-banks-always-win/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Getting Wukong and Pig Working Together on Amazon Elastic Map Reduce</title>
		<link>http://www.monkeyatlarge.com/archives/2011/03/16/getting-wukong-and-pig-working-together-on-amazon-elastic-map-reduce/</link>
		<comments>http://www.monkeyatlarge.com/archives/2011/03/16/getting-wukong-and-pig-working-together-on-amazon-elastic-map-reduce/#comments</comments>
		<pubDate>Wed, 16 Mar 2011 17:06:26 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Data]]></category>
		<category><![CDATA[Pig]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=416</guid>
		<description><![CDATA[Apache Pig is a great language for processing large amounts of data on a Hadoop cluster without delving into the minutiae of map reduce. Wukong is a great library to write map/reduce jobs for Hadoop from ruby. Together they can be really great, because problems unsolvable in pig without resorting writing a custom function in [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://pig.apache.org/">Apache Pig</a> is a great language for processing large amounts of data on a Hadoop cluster without delving into the minutiae of map reduce. </p>
<p><a href="https://github.com/mrflip/wukong">Wukong</a> is a great library to write map/reduce jobs for Hadoop from ruby. </p>
<p>Together they can be really great, because problems unsolvable in pig without resorting writing a custom function in Java can be solved by streaming data through an external script, which Wukong nicely wraps. The Data Chef blog has a <a href="http://thedatachef.blogspot.com/2011/02/brute-force-graph-crunching-with-pig.html">great example</a> of using Pig to choreograph the data flow, and ruby/wukong to compute Jaccard Similarity of sets.</p>
<h3>Working with Wukong on Elastic Map Reduce</h3>
<p>Elastic map reduce is a great resource &#8211; it&#8217;s very easy to quickly have a small hadoop cluster at your disposal to process some data. Getting wukong working requires an extra step: installing the wukong gem on all the machines in the cluster.</p>
<p>Fortunately, elastic map reduce allows the use of bootstrap scripts located on S3, which run on boot for all the machines in the cluster. I used the following script (based on an example on <a href="http://stackoverflow.com/questions/4336842/when-bootstrapping-an-amazon-elastic-map-reduce-job-can-my-script-use-sudo">stackoverflow</a>):</p>
<pre class="brush: plain; title: ; notranslate">
sudo apt-get update
sudo apt-get -y install rubygems
sudo gem install wukong --no-rdoc --no-ri
</pre>
<p>Using Amazon&#8217;s command line utility, starting the cluster ready to use in pig interactive mode looks like this</p>
<p>elastic-mapreduce &#8211;create  &#8211;bootstrap-action [S3 path to wukong-bootstrap.sh] &#8211;num-instances [a number] &#8211;slave-instance-type [ machine type ] &#8211;pig-interactive -ssh </p>
<p>The web tool for creating clusters has a space for specifying the path to a bootstrap script.</p>
<p>Next step: upload your pig script and it accompanying wukong script to the name node, and launch the job.  (It&#8217;s also possible to do all of that when starting the cluster with more arguments to elastic-map, with the added advantage that the cluster will terminate with your job)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2011/03/16/getting-wukong-and-pig-working-together-on-amazon-elastic-map-reduce/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>1080p ViewSonic monitor and OS X</title>
		<link>http://www.monkeyatlarge.com/archives/2011/03/08/1080p-viewsonic-monitor-and-os-x/</link>
		<comments>http://www.monkeyatlarge.com/archives/2011/03/08/1080p-viewsonic-monitor-and-os-x/#comments</comments>
		<pubDate>Tue, 08 Mar 2011 17:08:33 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=413</guid>
		<description><![CDATA[If you&#8217;re hooking up a Mac OS X machine to a 1080p monitor via a mini displayport to HDMI adapter, you may find your display settings doesn&#8217;t have a 1920&#215;1080 setting, and the 1080p setting produces an image with the edges cut off. Adjusting the overscan/underscan slider will make the image fit, but it turns [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;re hooking up a Mac OS X machine to a 1080p monitor via a mini displayport to HDMI adapter, you may find your display settings doesn&#8217;t have a 1920&#215;1080  setting, and the 1080p setting produces an image with the edges cut off. Adjusting the overscan/underscan slider will make the image fit, but it turns fuzzy.</p>
<p>Solution: check the monitor&#8217;s settings. In my ViewSonic VX2453 the HDMI inputs have 2 settings &#8220;AV&#8221; and &#8220;PC&#8221;. Switching it to PC solved the problem, and now the picture is exactly the right size and crisp.</p>
<p>I spent some time futzing around with SwitchRes and several fruitless reboots before discovering the setting, so I hope this saves someone time!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2011/03/08/1080p-viewsonic-monitor-and-os-x/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Redundant Indexing in PostgreSQL</title>
		<link>http://www.monkeyatlarge.com/archives/2011/02/08/redundant-indexing-in-postgresql/</link>
		<comments>http://www.monkeyatlarge.com/archives/2011/02/08/redundant-indexing-in-postgresql/#comments</comments>
		<pubDate>Tue, 08 Feb 2011 23:17:56 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=406</guid>
		<description><![CDATA[If you have a table with a column included as the first column in a multi-column index and then again with it&#8217;s own index, you may be over indexing. Postgres will use the multi-column index for queries on the first column. From the docs A multicolumn B-tree index can be used with query conditions that [...]]]></description>
			<content:encoded><![CDATA[<p>If you have a table with a column included as the first column in a multi-column index and then again with it&#8217;s own index, you may be over indexing. Postgres will use the multi-column index for queries on the first column. </p>
<p>From the <a href="http://www.postgresql.org/docs/8.4/static/indexes-multicolumn.html">docs</a></p>
<blockquote><p>A multicolumn B-tree index can be used with query conditions that involve any subset of the index&#8217;s columns, but the index is most efficient when there are constraints on the leading (leftmost) columns.</p></blockquote>
<p><strong><br />
Performance</strong></p>
<p>If you click around that section of the docs, you&#8217;ll surely come across the section on multi-column indexing and performance, in particular this <a href="http://www.postgresql.org/docs/8.4/static/indexes-bitmap-scans.html">section</a> (bold emphasis mine):</p>
<blockquote><p>You could also create a multicolumn index on (x, y). This index would typically be more efficient than index combination for queries involving both columns, but as discussed in Section 11.3, it would be almost useless for queries involving only y, so it should not be the only index. A combination of the multicolumn index and a separate index on y would serve reasonably well. <strong>For queries involving only x, the multicolumn index could be used, though it would be larger and hence slower than an index on x alone</strong></p></blockquote>
<p>Life is full of tradeoffs performance wise, so we should explore just how much slower it is to use a multi-column index for single column queries.</p>
<p>First, lets create a dummy table:</p>
<pre class="brush: sql; title: ; notranslate">
CREATE TABLE foos_and_bars
(
  id serial NOT NULL,
  foo_id integer,
  bar_id integer,
  CONSTRAINT foos_and_bars_pkey PRIMARY KEY (id)
)
</pre>
<p>Then, using R, we&#8217;ll create 3 million rows of nicely distributed data:</p>
<pre class="brush: r; title: ; notranslate">
rows = 3000000
foo_ids = seq(1,250000,1)
bar_ids = seq(1,20,1)
data = data.frame(foo_id = sample(foo_ids, rows,TRUE), bar_id= sample(bar_ids,rows,TRUE))
</pre>
<p>Dump that to a text file and load it up with \copy and we&#8217;re good to go.</p>
<p>Create the compound index</p>
<pre class="brush: sql; title: ; notranslate">
CREATE INDEX foo_id_and_bar_id_index
  ON foos_and_bars
  USING btree
  (foo_id, bar_id);
</pre>
<p>Run a simple query to make sure the index is used:</p>
<pre class="brush: plain; title: ; notranslate">
test_foo=# explain analyze select * from foos_and_bars where foo_id = 123;
                                                           QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on foos_and_bars  (cost=4.68..55.74 rows=13 width=12) (actual time=0.026..0.038 rows=8 loops=1)
   Recheck Cond: (foo_id = 123)
   -&gt;  Bitmap Index Scan on foo_id_and_bar_id_index  (cost=0.00..4.68 rows=13 width=0) (actual time=0.020..0.020 rows=8 loops=1)
         Index Cond: (foo_id = 123)
 Total runtime: 0.072 ms
(5 rows)
</pre>
<p>Now we&#8217;ll make 100 queries by foo_id with this index, and then repeat with the single index installed using this code:</p>
<pre class="brush: ruby; title: ; notranslate">
require 'rubygems'
require 'benchmark'
require 'pg'

TEST_IDS = [...] #randomly selected 100 ids in R

conn = PGconn.open(:dbname =&gt; 'test_foo')
def perform_test(conn,foo_id)
  time = Benchmark.realtime do
    res = conn.exec(&quot;select * from foos_and_bars where foo_id = #{foo_id}&quot;)
    res.clear
  end
end

TEST_IDS.map {|id| perform_test(conn,id)} #warm things up?
data = TEST_IDS.map {|id| perform_test(conn,id)}

data.each do |d|
puts d
end
</pre>
<p>How do things stack up? I&#8217;d say about evenly:</p>
<p><a href="http://www.monkeyatlarge.com/blog/wp-content/uploads/2011/02/query-perf-comparison.png"><img src="http://www.monkeyatlarge.com/blog/wp-content/uploads/2011/02/query-perf-comparison.png" alt="" title="query-perf-comparison" width="569" height="452" class="aligncenter size-full wp-image-410" /></a></p>
<p>Remember: Indexing isn&#8217;t free, and Postgres is pretty good at using (and reusing) your indexes, so you may not need to create as many as you think. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2011/02/08/redundant-indexing-in-postgresql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OSX VPN Problems: Kill the racoon</title>
		<link>http://www.monkeyatlarge.com/archives/2011/01/18/osx-vpn-problems-kill-the-racoon/</link>
		<comments>http://www.monkeyatlarge.com/archives/2011/01/18/osx-vpn-problems-kill-the-racoon/#comments</comments>
		<pubDate>Tue, 18 Jan 2011 16:48:22 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=402</guid>
		<description><![CDATA[Occasionally my Mac will refuse to connect to work&#8217;s IPSec VPN with the error message: &#8220;A configuration error occured. Verify your settings and try reconnecting&#8221; This usually happens to me after a long time between reboots, and a reboot usually allows me to successfully connect again. Rebooting when I&#8217;m in the middle of something can [...]]]></description>
			<content:encoded><![CDATA[<p>Occasionally my Mac will refuse to connect to work&#8217;s IPSec VPN with the error message:<br />
&#8220;A configuration error occured.  Verify your settings and try reconnecting&#8221;</p>
<p>This usually happens to me after a long time between reboots, and a reboot usually allows me to successfully connect again. Rebooting when I&#8217;m in the middle of something can be a pain, so I did some research and found a better way. There&#8217;s a process called &#8220;racoon&#8221; &#8211; it performs key exchange operations to set up IPSec tunnels. Kill it (using kill or activity monitor) and your VPN will start working again.</p>
<p>Works on OSX 10.6.5 and 10.6.6</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2011/01/18/osx-vpn-problems-kill-the-racoon/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>(Ab)using memoize to quickly solve tricky n+1 problems</title>
		<link>http://www.monkeyatlarge.com/archives/2010/12/08/abusing-memoize-to-quickly-solve-tricky-n1-problems/</link>
		<comments>http://www.monkeyatlarge.com/archives/2010/12/08/abusing-memoize-to-quickly-solve-tricky-n1-problems/#comments</comments>
		<pubDate>Thu, 09 Dec 2010 03:41:23 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=385</guid>
		<description><![CDATA[Usually, discovering n+1 problems in your Rails application that can&#8217;t be fixed with an :include statement means lots of changes to your views. Here&#8217;s a workaround that skips the view changes that I discovered working with Rich to improve performance of some Dribbble pages. It uses memoize to convince your n model instances that they [...]]]></description>
			<content:encoded><![CDATA[<p>Usually, discovering n+1 problems in your Rails application that can&#8217;t be fixed with an :include statement means lots of changes to your views. Here&#8217;s a workaround that skips the view changes that I discovered working with <a href="http://twitter.com/frogandcode">Rich</a> to improve performance of some <a href="http://dribbble.com/">Dribbble</a> pages.  It uses memoize to convince your n model instances that they already have all the information needed to render the page.</p>
<p>While simple belongs_to relationships are easy to fix with :include, lets take a look at a concrete example where that won&#8217;t work:</p>
<pre class="brush: ruby; light: true; title: ; notranslate">
class User &lt; ActiveRecord::Base
  has_many :likes
end

class Item &lt; ActiveRecord::Base
  has_many :likes
  def liked_by?(user)
     likes.by_user(user).present?
  end
end

class Like &lt; ActiveRecord::Base
  belongs_to :user
  belongs_to :item
end
</pre>
<p>A view presenting a set of items that called Item#liked_by? would be an n+1 problem that wouldn&#8217;t be well solved by :include.  Instead, we&#8217;d have to come up with a query to get the Likes for the set of items by this user:</p>
<pre class="brush: ruby; light: true; title: ; notranslate">
Like.of_item(@items).by_user(user)
</pre>
<p>Then we&#8217;d have to store that in a controller instance variable, and change all the views that called item.liked_by?(user) to access the instance variable instead.</p>
<p>Active Support&#8217;s memoize functionality stores the results of function calls so they&#8217;re only evaluated once. What if we could trick the method into thinking it&#8217;s already been called? We can do just that by writing data into the instance variables that memoize uses to save results on each of the model instances. First, we memoize liked_by:</p>
<pre class="brush: ruby; light: true; title: ; notranslate">
  memoize :liked_by?
</pre>
<p>Then bulk load the relevant likes and stash them into memoize&#8217;s internal state:</p>
<pre class="brush: ruby; light: true; title: ; notranslate">
def precompute_data(items, user)
  likes = Like.of_item(items).by_user(user).index_by {|like| like.item_id}
  items.each do |item|
    item.write_memo(:liked_by?,likes[item.id].present?,user)
  end
end
</pre>
<p>The write_memo method is implemented as follows.</p>
<pre class="brush: ruby; light: true; title: ; notranslate">
  def write_memo(method, return_value, args=nil)
    ivar = ActiveSupport::Memoizable.memoized_ivar_for(method)
    if args
      if hash = instance_variable_get(ivar)
        hash[Array(args)] = return_value
      else
        instance_variable_set(ivar, {Array(args) =&gt; return_value})
      end
    else
      instance_variable_set(ivar, [return_value])
    end
  end
</pre>
<p>This problem described here could be solved with some crafty left joins added to the query that fetched the items in the first place, but when there&#8217;s several different hard to prefetch properties, such a query would likely become unmanageable, if not terribly slow.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2010/12/08/abusing-memoize-to-quickly-solve-tricky-n1-problems/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Idiot Calling on Twitter &#8211; Frequency of You&#8217;re vs Your</title>
		<link>http://www.monkeyatlarge.com/archives/2010/12/02/idiot-calling-on-twitter-frequency-of-youre-vs-your/</link>
		<comments>http://www.monkeyatlarge.com/archives/2010/12/02/idiot-calling-on-twitter-frequency-of-youre-vs-your/#comments</comments>
		<pubDate>Fri, 03 Dec 2010 00:54:47 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Data]]></category>
		<category><![CDATA[Musings]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=380</guid>
		<description><![CDATA[At the risk of being forever branded a grammar elitist, lets take a quick look at use of the phrase &#8220;your an idiot&#8221; on twitter. Inspired by the tweet by @doctorzaius referencing a URL to Twitter&#8217;s search page for &#8220;your an idiot&#8221;, I used Twitter&#8217;s streaming API to download a sample of 6581 tweets containing [...]]]></description>
			<content:encoded><![CDATA[<p>At the risk of being forever branded a grammar elitist, lets take a quick look at use of the phrase &#8220;your an idiot&#8221; on twitter. </p>
<p>Inspired by the <a href="http://twitter.com/#!/doctorzaius/status/10061828855963648 ">tweet</a> by <a href="http://twitter.com/#!/doctorzaius/">@doctorzaius</a> referencing a URL to Twitter&#8217;s <a href="http://search.twitter.com/search?q=%22your+an+idiot%22">search page for &#8220;your an idiot&#8221;</a>, I used Twitter&#8217;s streaming API to download a sample of 6581 tweets containing the word &#8220;idiot&#8221; overnight, for about 12 hours.</p>
<p>Of these 6581 tweets, 65 contained our friend &#8220;your an idiot&#8221;. 161, two and a half times as many, contained &#8220;you&#8217;re an idiot&#8221;. Additionally, there were 2 tweets with &#8220;your such an idiot&#8221;, and just one &#8220;you&#8217;re such an idiot&#8221;. The forces of good grammar have won this round?</p>
<p>Note: This is a very small sample. It may be interesting to compare Facebook status updates to see what the you&#8217;re/your ratio looks like there one day&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2010/12/02/idiot-calling-on-twitter-frequency-of-youre-vs-your/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>FluidSurveys Data Export Issue, Solved with iconv</title>
		<link>http://www.monkeyatlarge.com/archives/2010/11/24/fluidsurveys-data-export-issue-solved-with-iconv/</link>
		<comments>http://www.monkeyatlarge.com/archives/2010/11/24/fluidsurveys-data-export-issue-solved-with-iconv/#comments</comments>
		<pubDate>Wed, 24 Nov 2010 16:28:35 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=373</guid>
		<description><![CDATA[I recently ran a survey at work using FluidSurveys. Their survey building tools are excellent, and they have great support, but I ran into a time consuming issue when it came time to process the responses because they&#8217;re double byte unicode, UTF-16LE to be specific. Turns out knowing that is 90% of the battle. The [...]]]></description>
			<content:encoded><![CDATA[<p>I recently ran a survey at work using <a href="http://fluidsurveys.com/">FluidSurveys</a>.  Their survey building tools are excellent, and they have great support, but I ran into a time consuming issue when it came time to process the responses because they&#8217;re double byte unicode, UTF-16LE to be specific. Turns out knowing that is 90% of the battle.</p>
<p>The files on first inspection are a bit strange, because although they spring from a csv export button, they&#8217;re tab-delimited, but with CSV-style quoting conventions. That&#8217;s easy enough to work around, but R and Ruby both barfed reading the files. I cottoned on to the fact that the files had some odd characters in them, so I recruited JRuby and ruby 1.9 to try to load them, due to better unicode support, but still couldn&#8217;t quite get the parameters right.</p>
<p>Then I thought of <a href="http://www.gnu.org/software/libiconv/">iconv</a>, the character set converting utility. Since in this case, the only special characters was the ellipsis character, I was happy to strip those out, and the following command does the trick:</p>
<pre class="brush: bash; light: true; title: ; notranslate">
iconv -f UTF-16LE -t US-ASCII -c responses.csv &gt; converted_responses.csv
</pre>
<p>And, as they say, Bob&#8217;s your uncle</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2010/11/24/fluidsurveys-data-export-issue-solved-with-iconv/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

