<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom">

 <title>Richard Huang</title>
 
 <link href="http://www.huangzhimin.com/" />
 <updated>2013-06-18T22:56:36+08:00</updated>
 <id>http://www.huangzhimin.com/</id>
 <author>
   <name>Richard Huang</name>
   <email>flyerhzm@gmail.com</email>
 </author>

 
 <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/richard-huang" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="richard-huang" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bloglines.com/sub/http://feeds.feedburner.com/richard-huang" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2Frichard-huang" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><entry>
   <title>How to render, upload and download large files on heroku with s3</title>
   <link href="http://www.huangzhimin.com/2013/06/18/how-to-upload-and-download-large-files-on-heroku-with-s3/" />
   <updated>2013-06-18T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2013/06/18/how-to-upload-and-download-large-files-on-heroku-with-s3</id>
   <content type="html">&lt;p&gt;I'm consulting on a rails project on heroku, it involves generating a
large pdf for customer, so you must already guess it led to 30s timeout
on heroku.&lt;/p&gt;

&lt;p&gt;At first, I handled it with common sense, moving pdf render to a
background job, in the client side, it polls the status of bj, if job is
complete, then render the pdf.&lt;/p&gt;

&lt;p&gt;Everything works fine on my laptop, but after pushing to heroku, it
succeed to running then job, polling the status, but finally it can't
find the generated pdf. Then I realized web dyno and worker dyno are
running on different servers, that means web dyno can't find the pdfs
which are generated on worker dyno. Okay, we need a cloud storage
service, of course, s3 is the first choice.&lt;/p&gt;

&lt;p&gt;I used aws-sdk as the s3 client, it's pretty easy to upload pdf to s3,
as pdfs are private on s3, it has to download pdf and render to client
after polling successfully. The timeout problem still exists if the pdf
file is large or the network is not good. (take a long time to render
pdf content to client)&lt;/p&gt;

&lt;p&gt;After googling some solutions, I decided to use &lt;a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html"&gt;S3 Temporary Security
Credentials&lt;/a&gt;, it creates a resource url with a temporary credential,
you can set the expire date for the resource, it sacrifices some
privacy, the resources are still private, but they can be accessed by
the url with a temporary credential, we set the expire date to 1 hour
later, so it's not a big deal.&lt;/p&gt;

&lt;p&gt;Resource url with temporary security credential doens't exist in aws-sdk
gem, so I have to implement it by myself.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;openssl&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;digest/sha1&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;base64&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;signed_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expire_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;OpenSSL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sha1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;string_to_sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;GET&lt;/span&gt;&lt;span class="se"&gt;\n\n\n&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;expire_date&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;hmac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;OpenSSL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:HMAC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;S3_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string_to_sign&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CGI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;https://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.s3.amazonaws.com/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?AWSAccessKeyId=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;S3_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;Expires=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;expire_date&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;Signature=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It can generate signed url for different resources with different expire
date, now I just tell client the signed_url and the client just render
the pdf from s3 rather than heroku, so no timeout anymore, awesome!&lt;/p&gt;

&lt;p&gt;So the whole process is as follows&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client clicks "Print PDF" button, it sends a request to web dyno.&lt;/li&gt;
&lt;li&gt;Web dyno asks worker dyno render pdf. i&lt;/li&gt;
&lt;li&gt;Client keeps polling the job status.&lt;/li&gt;
&lt;li&gt;Worker dyno render the pdf and uploads to s3, generates a signed url.&lt;/li&gt;
&lt;li&gt;Client gets the jos complete message with s3 signed url.&lt;/li&gt;
&lt;li&gt;Client print the pdf from s3.&lt;/li&gt;
&lt;/ol&gt;

&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/T3QV9oVRGLo" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>My presentation at reddotrubyconf 2013</title>
   <link href="http://www.huangzhimin.com/2013/06/07/my-presentation-at-reddotrubyconf-2013/" />
   <updated>2013-06-07T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2013/06/07/my-presentation-at-reddotrubyconf-2013</id>
   <content type="html">&lt;p&gt;This is my presentation on reddotrubyconf 2013 with notes, building
asynchronous apis.&lt;/p&gt;

&lt;script async="true" class="speakerdeck-embed" data-id="cd157a80b1700130a9093242a473c411" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;


&lt;p&gt;3. Several years ago when I started learning rails, many people said rails was not fast, but it can significantly speed up development, the famous words are "Hardware is cheap, Programmers are expensive".&lt;/p&gt;

&lt;p&gt;4. It is true when your business is still young, at first, you may have only one server, then you can buy more servers, distribute web, app and db to different servers, it's the easiest way to handle more traffic.&lt;/p&gt;

&lt;p&gt;5. but...&lt;/p&gt;

&lt;p&gt;6. one day your business gets successful, more and more users will bring more and more traffics, you have to buy more and more web servers, app servers and db servers.&lt;/p&gt;

&lt;p&gt;7. At this stage, machines are not cheap any more, so programmers try to find ways to improve server performance, increase concurrency and reduce machines.&lt;/p&gt;

&lt;p&gt;8. So you see linkedin moved from rails to node, 27 servers cut and up to 20x faster.&lt;/p&gt;

&lt;p&gt;9. Iron.io mvoed from rails to go, 28 servers cut.&lt;/p&gt;

&lt;p&gt;10. Does it mean ruby or rails is so slow? Does it mean we should drop ruby and use node.js or go instead?&lt;/p&gt;

&lt;p&gt;11. From my experience, the answer is no, this is what I want to share with you today.&lt;/p&gt;

&lt;p&gt;12. During my last job, I'm lucky that I have the opportunity to build the same leaderboard apis service running with multi processes, multi threads and asynchronous non blocking io ruby servers. At first we build the api service with rails and ree, db is mysql, the average response time is 50 ms, and it handled 60k rpm on production with 13 machines, 6 passenger instances per machine. After that we migrated to JRuby 1.7.0, it introduced 40% performance improved, average response time decreased to 30 ms, it was same to handle 60k rpm on production, but with only 10 machines, 1 torquebox instance with 5 threads per machine. Finally, we rewrote the api service, using ruby 1.9.3, and used goliath, which is a non-blocking ruby web server, switched db to redis, the average response time decrease to 4 ms, and handled 240k rpm on production with only 4 machines, 4 goliath instances each machine. If old rails api service also handled 240k rpm, it needs 52 machines.&lt;/p&gt;

&lt;p&gt;13. So I can say moving from ruby to ruby, 48 servers cut and 10 times faster.&lt;/p&gt;

&lt;p&gt;14. We run the api service with rails synchronously, all IO operations are blocked, but run with goliath is asynchronous, and IO operations are nonblocking. It's a bit unfair to compare the performance directly between Rails and Goliath, or Rails and Node.js, but it's good to know that building api service with asynchronous nonblocking io can significantly increase the concurrency.&lt;/p&gt;

&lt;p&gt;15. How blocking IO works? e.g. when a request is coming, a process gets the cpu time, run the code, but when it calls database query, cpu has to wait for it to complete, we all know IO operations are slow, the blocking IO will block the whole process.&lt;/p&gt;

&lt;p&gt;16. In multi processes model, when cpu is blocked in process A, it will schedule from process A to process B, keep running, when IO operation is completed in process A, cpu will schedule back to process A and continue working.&lt;/p&gt;

&lt;p&gt;17. The advantages of multi processes are the multi processes can be executed in true parallel on multi cores cpu. Running with multi processes model is easy to manage, we can start or stop a processor by sending a signal. The disadvantages of multiple processes are process switching is expensive, it involves switching out all of the process resources. It also consumes many memory, multiple processes means multiple memory copy.&lt;/p&gt;

&lt;p&gt;18. Multi threads model is similar to multi processes model, when cpu is blocked in thread A, it will schedule from thread A to thread B in one process.&lt;/p&gt;

&lt;p&gt;19. The advantages of multi threads are threads switching is cheap, it involves switching out only the resources unique between threads. As many resources are shared between threads in one process, multi threads consume much less memory. The disadvantages are if you share mutable data across threads, you need to synchronize access to that data for thread safety, this will affect performance and concurrency. With ruby 1.9 or 2.0, GIL is still there, that means only one thread can handle request at a time. The exception is JRuby and Rubinius which already removed GIL and can make use of multi cores.&lt;/p&gt;

&lt;p&gt;20. Evented model is running with a main loop, and never blocked, all io operations are asynchronous, when calling an io operation, instead of waiting, the main loop can process other requests, and come back when the response from io call is ready.&lt;/p&gt;

&lt;p&gt;21. The advantages of the evented model are there is no blocking io, no context switching and it consumes least memory usage comparing to multi processes and multi threads models. The disadvantage is your code will be full of callbacks, make it difficult to understand.&lt;/p&gt;

&lt;p&gt;22. In ruby world, we usually use eventmachine to implement non blocking io, but it's very common to write many nested callbacks, like this.&lt;/p&gt;

&lt;p&gt;23. Good luck, ruby 1.9 introduces fiber, and a gem, named em-synchrony, fiber aware eventmachine can help solve too many callbacks issue.&lt;/p&gt;

&lt;p&gt;24. The code works same as the last example, using em-synchrony, but no callbacks and more readable.&lt;/p&gt;

&lt;p&gt;25. Let’s clarify the definitions of concurrency and parallelism, concurrency performs 2 operations in tandem, while parallelism performs 2 operations at the same time.&lt;/p&gt;

&lt;p&gt;26. Evented model is used to increase concurrency in one processor, so in practice, we will use multi processes with evented in order to utilize all of the cores on your CPU.&lt;/p&gt;

&lt;p&gt;27. We already talked why we should use async non-blocking IO, but how? I wrote a project on github named apis-bench, it implements a simple leaderboard apis service with multiple framework and run on multi processes, multi threads and asynchronous non blocking ruby server.&lt;/p&gt;

&lt;p&gt;28. Assume we use rails to build the apis service, router dispatches the request to controller, controller creates, reads, updates or destroys models, then controller generate a json view and sends the response.&lt;/p&gt;

&lt;p&gt;29. A good practice is skinny controller and fat model, so I write most logic in models.&lt;/p&gt;

&lt;p&gt;30. Write the controller as simple as possible, and use respond_to / respond_with to render a json response.&lt;/p&gt;

&lt;p&gt;31. Here is the router.&lt;/p&gt;

&lt;p&gt;32. Instead of migrating asynchronous io directly, let's do a small step, migrating rails to grape framework. Grape is a micro framework to build REST apis, using grape instead of rails controller can decrease response time.&lt;/p&gt;

&lt;p&gt;33. Grape is responsible for router, controller and view, gets request, ask model to do something and then render response.&lt;/p&gt;

&lt;p&gt;34. Most developers prefer using activerecord, it provides many powerful ways to develop models rapidly. We can use activerecord without rails, so here, we don't need to do any change in model layer.&lt;/p&gt;

&lt;p&gt;35. Grape provides its own DSL, we have to use Grape::API replace rails controller api, but we already followed the skinny controller practice, it should not be too much work to do.&lt;/p&gt;

&lt;p&gt;36. Next step, let's migrate to asynchronous non-blocking io, goliath is a non-blocking ruby web server framework, adding goliath can significantly increase the throughput.&lt;/p&gt;

&lt;p&gt;37. Here, each HTTP request is handled by goliath, request is executed within a ruby fiber, then goliath proxies request to grape, all IO operations are asynchronous.&lt;/p&gt;

&lt;p&gt;38. In this migration, we also no need to change any code except adding goliath api, and telling it delegate request to grape api, very simple.&lt;/p&gt;

&lt;p&gt;39. Besides we must replace our existing blocking io libraries to eventmachine's libraries, like mysql2 to em_mysql2, mongo to em_mongo, etc.&lt;/p&gt;

&lt;p&gt;40. We have another option besides grape, it's sinatra, it can also decrease the response time.&lt;/p&gt;

&lt;p&gt;41. Similar to grape, sinatra takes care of router, controller and view.&lt;/p&gt;

&lt;p&gt;42. The only place we should change from rails to sinatra is the controller, here we define the route, action and render json&lt;/p&gt;

&lt;p&gt;43. After using sinatra, it's also easy to migrate to asynchronous non blocking io by adding sinatra-synchrony, as its name implies, sinatra-synchrony adds em-synchrony to sinatra.&lt;/p&gt;

&lt;p&gt;44. sinatra-synchrony is not a web server, so we have to use an event ruby server, like thin, then sinatra-synchrony executes each http request within a ruby fiber just like goliath.&lt;/p&gt;

&lt;p&gt;45. Adding sinatra-synchrony is also easy, what you need to do is only register Sinatra::Synchrony as an extension, then it works.&lt;/p&gt;

&lt;p&gt;46. Same, you need to replace blocking io library with eventmachine's libraries.&lt;/p&gt;

&lt;p&gt;47. Finally, let's see the benchmark result.&lt;/p&gt;

&lt;p&gt;48. The first benchmark test is a CPU bound action, db time takes about 10% total response time, it's tested with apache benchmark.&lt;/p&gt;

&lt;p&gt;49. I tested with several groups, sending 1000 requests with 10 concurrency, 50 concurrency, 100 concurrency, 200 concurrency, 500 concurrency and last one is sending 2000 requests with 1000 concurrency, in each group, I tried rails, sinatra, grape, sinatra with threads, grape with threads, sinatra-synchrony with thin and grape with goliath, they all run in a single process, rails, sinatra and grape are running in unicorn, sinatra with threads and grape with thread are running in rainbows. The value here is the time taken for completing all requests.  As you have seen, sinatra api is 40% faster than rails api and grape api is 30% faster than rails api, sinatra threads and grape threads in this case, about 2 times slower, it should perform better when running threads with jruby. Asynchrous non blocking io performs best, especially sinatra-synchrony. When sending requests with 200 concurrency, rails, sinatra, grape, sinatra threads and grape threads are all timed out, they are failed to handle so many requests, but grape with goliath and sinatra-synchrony with thin can handle 1000 concurrency without any errors.
With CPU bound actions, threads didn't perform well, but asynchronous non blocking io works can handle much more requests.&lt;/p&gt;

&lt;p&gt;50. What about the IO bound action? the following test is an action, in which db time takes more than 90% total response time, I sent the requests with stable rates and measured the performance on newrelic.&lt;/p&gt;

&lt;p&gt;51. I sent 1200 rpm to rails api, but it can only handle 984 rpm, others returns 502 timed out error.&lt;/p&gt;

&lt;p&gt;52. Sinatra api can only handle 1080 rpm.&lt;/p&gt;

&lt;p&gt;53. and grape api handle 1130 rpm, all of them failed to handle 1200 rpm.&lt;/p&gt;

&lt;p&gt;54. I set rainbows work with 200 threads and 200 connections in db pool, sinatra threads successfully handle 3000 rpm.&lt;/p&gt;

&lt;p&gt;55. Grape threads also succeed.&lt;/p&gt;

&lt;p&gt;56. grape with goliath passed as well.&lt;/p&gt;

&lt;p&gt;57. I failed to add newrelic with sinatra-synchrony, but it also succeed to handle 3000 rpm. Another known issue is I failed to add fiber_pool to goliath, I appreciate it if you can help to solve the issue and open a pull request to me.&lt;/p&gt;

&lt;p&gt;58. Okay, the conclustion is Rails is good, it's still a good choice to build apis service rapidly, we can migrate rails to sinatra or grape to decrease response time, then migrate to sinatra-synchrony or goliath to increase the throughput, finally I have to say asynchronous non-blocking io is awesome, you should give it a try.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/6XmiUAQFals" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>another zero downtime deployment solution</title>
   <link href="http://www.huangzhimin.com/2013/01/30/another-zero-downtime-deployment-solution/" />
   <updated>2013-01-30T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2013/01/30/another-zero-downtime-deployment-solution</id>
   <content type="html">&lt;p&gt;I wrote &lt;a href="http://huangzhimin.com/2012/11/14/jruby-at-openfeint-jruby-migration-success-story/"&gt;a post&lt;/a&gt; for jruby migration 2 monthes ago, it mentioned a
solution to do zero downtime deployment: pull out server out of load
balancers, restart server, and then put in the server. It works but has
some cons&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;you must have more than 1 app hosts.&lt;/li&gt;
&lt;li&gt;deployment process gets much slower if you have lots of app hosts.&lt;/li&gt;
&lt;li&gt;you lost one host's throughput during deployment.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;I'm using a different solution for zero downtime deployment now, instead
of processing app hosts one by one&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;it starts replicated ruby instances on all app hosts.&lt;/li&gt;
&lt;li&gt;reload load balancer (proxy) to send traffic to replicated ruby
instances.&lt;/li&gt;
&lt;li&gt;stops original ruby instances.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;It won't slow down your deployment process, it also works well if
you only have 1 app host and you don't lost any throughput during
deployment.&lt;/p&gt;

&lt;p&gt;The disadvantage is it needs more memory on your app host, it occupies
x2 ruby instances' memory during deployment. Our project is an api
service built on ruby not rails, memory usage is pretty low, only 50 mb
per ruby instance, so x2 memory usage is not a big deal.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/RrD8cX2a5cE" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>another redis automatic failover solution for ruby</title>
   <link href="http://www.huangzhimin.com/2013/01/13/another-redis-automatic-failover-solution-for-ruby/" />
   <updated>2013-01-13T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2013/01/13/another-redis-automatic-failover-solution-for-ruby</id>
   <content type="html">&lt;p&gt;Redis gets more and more popular as a backend storage, so the redis
failover solution becomes important before you use redis as a critical
resource.&lt;/p&gt;

&lt;p&gt;Currently the most popular automatic master/slave failover solution for
ruby is &lt;a href="https://github.com/ryanlecompte/redis_failover"&gt;redis_failover&lt;/a&gt;, it's based on ZooKeeper, if you already
have ZooKeeper in your infrastructure, it's great.&lt;/p&gt;

&lt;p&gt;But I noticed that redis already has a built-in automatic failover
solution, called &lt;a href="http://redis.io/topics/sentinel"&gt;Redis Sentinel&lt;/a&gt;. In case you didn't heard of it,
please read the official document, it's simple and no other external
dependency. I searched on github, but none was working well. I have
to implement it by myself.&lt;/p&gt;

&lt;p&gt;The key point is you never connect to redis master server directly.
Instead, you talk to redis sentinel servers, ask them where is the
master server, and then connect to the redis master server.&lt;/p&gt;

&lt;p&gt;When your redis master server down, your redis sentinel servers will
tell you a new master server, so you just disconnect old server and
connect to new master server&lt;/p&gt;

&lt;p&gt;My soluion is a monkey-patch to redis-rb gem, it's &lt;a href="https://github.com/flyerhzm/redis-sentinel"&gt;redis-sentinel&lt;/a&gt;,
before it tries to connect redis server, it firstly asks redis
sentinels where is master server, then connect as usual. Try it and give
me the feedback.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/cooc4UfVXCI" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>newrelic-grape released</title>
   <link href="http://www.huangzhimin.com/2012/12/21/newrelic-grape-released/" />
   <updated>2012-12-21T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/12/21/newrelic-grape-released</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;No instrumentation, no performance tuning!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is my first time to use &lt;a href="https://github.com/intridea/grape"&gt;grape&lt;/a&gt; to build an api service, grape
repo has more than 2k watchers, but I'm surprised there is no existing
newrelic grape suppport, I just found some gists to do it, and this
&lt;a href="http://artsy.github.com/blog/2012/11/29/measuring-performance-in-grape-apis-with-new-relic/"&gt;blog post&lt;/a&gt; gave me the idea to add newrelic instrument as grape
middleware, but it's not the standard way newrelic recommends.&lt;/p&gt;

&lt;p&gt;So I released &lt;a href="https://github.com/flyerhzm/newrelic-grape"&gt;newrelic-grape&lt;/a&gt; gem to help you integrate newrelic
into grape.&lt;/p&gt;

&lt;p&gt;What you need to do is&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;newrelic-grape&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;rpm_contrib&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and monitor the performance on newrelic.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/cf1-Tpvulw0" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>How I find out a memory leak in grape</title>
   <link href="http://www.huangzhimin.com/2012/12/16/how-i-find-out-a-memory-leak-in-rack-app/" />
   <updated>2012-12-16T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/12/16/how-i-find-out-a-memory-leak-in-rack-app</id>
   <content type="html">&lt;p&gt;I'm helping my customer build a high performance api service these
weeks, we are close to release, but when I did load test this Wednesday,
I found the memory kept growing when I sent traffic and never went down,
it was obviously a memory leak.&lt;/p&gt;

&lt;p&gt;Lucky is I can reproduce the memory leak on my local machine, so I can
detect it easily. Our api service is simple, only contains model layer
(AR and redis) and api layer (based on &lt;a href="https://github.com/intridea/grape"&gt;grape&lt;/a&gt;). At first, I disabled
model layer, but memory leak was still there, so I was pretty sure the
leak was in api layer.&lt;/p&gt;

&lt;p&gt;Memory leak is always not easy to find, especially when I'm not sure
where it is, in my own code or some dependent libraries I used. I need
some tools' help.&lt;/p&gt;

&lt;p&gt;First, I used &lt;a href="https://github.com/Vasfed/heap_dump"&gt;heap_dump&lt;/a&gt; to dump the ruby heap memory after sending
10 minutes' traffic, and searched the keywords used in our repository, I
noticed every request path string resided in memory, why? Was there an
array or hash used them? heap_dump can't answer me.&lt;/p&gt;

&lt;p&gt;Then I tried ruby 1.9.3 ObjectSpace to find more info. I changed
Grape::API.call behavior, printing live objects for each request.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_with_gc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;GC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;
  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call_without_gc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;ObjectSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count_objects&lt;/span&gt;
  &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The followings are parts of the result&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="o"&gt;{&lt;/span&gt;:TOTAL&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;331126, :FREE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;218067, :T_OBJECT&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;3339, :T_CLASS&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;3394, :T_MODULE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;474, :T_FLOAT&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;195, :T_STRING&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;55324, :T_REGEXP&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;1135, :T_ARRAY&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;20188, :T_HASH&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;926, :T_STRUCT&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;125, :T_BIGNUM&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;22, :T_FILE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;4, :T_DATA&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;16011, :T_MATCH&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;13, :T_COMPLEX&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;1, :T_RATIONAL&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;33, :T_NODE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;11273, :T_ICLASS&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;602&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;23153:INFO&lt;span class="o"&gt;]&lt;/span&gt; 2012-12-16 21:59:55 :: Status: 200, Content-Length: 19, Response Time: 42.43ms
&lt;span class="o"&gt;{&lt;/span&gt;:TOTAL&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;331126, :FREE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;218066, :T_OBJECT&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;3339, :T_CLASS&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;3394, :T_MODULE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;474, :T_FLOAT&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;195, :T_STRING&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;55325, :T_REGEXP&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;1135, :T_ARRAY&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;20188, :T_HASH&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;926, :T_STRUCT&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;125, :T_BIGNUM&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;22, :T_FILE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;4, :T_DATA&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;16011, :T_MATCH&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;13, :T_COMPLEX&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;1, :T_RATIONAL&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;33, :T_NODE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;11273, :T_ICLASS&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;602&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;23153:INFO&lt;span class="o"&gt;]&lt;/span&gt; 2012-12-16 21:59:56 :: Status: 200, Content-Length: 20, Response Time: 43.29ms
&lt;span class="o"&gt;{&lt;/span&gt;:TOTAL&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;331126, :FREE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;218065, :T_OBJECT&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;3339, :T_CLASS&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;3394, :T_MODULE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;474, :T_FLOAT&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;195, :T_STRING&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;55326, :T_REGEXP&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;1135, :T_ARRAY&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;20188, :T_HASH&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;926, :T_STRUCT&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;125, :T_BIGNUM&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;22, :T_FILE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;4, :T_DATA&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;16011, :T_MATCH&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;13, :T_COMPLEX&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;1, :T_RATIONAL&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;33, :T_NODE&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;11273, :T_ICLASS&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;602&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;23153:INFO&lt;span class="o"&gt;]&lt;/span&gt; 2012-12-16 21:59:57 :: Status: 200, Content-Length: 20, Response Time: 45.74ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As you can see, every request, there was a string couldn't be garbage
collected, but I still didn't know where it was. I commented my logic
code in api layer, just returned an empty json, and string leak still
existed, then I went to grape source code, commented the code in
&lt;a href="https://github.com/intridea/grape/blob/v0.2.2/lib/grape/api.rb#L49"&gt;Grape::API#call&lt;/a&gt; method, updated as following code&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;After that, the string memory leak disappeared, It was a strong
possibility that memory leak was in grape gem.&lt;/p&gt;

&lt;p&gt;Next thing was pretty easy, tried to replace grape middleware one by one,
grape middleware has 3 methods, call!, before and after, will be called
in every request, I replaced all of them to figure out leak.&lt;/p&gt;

&lt;p&gt;Finally, I found it's &lt;a href="https://github.com/intridea/grape/blob/v0.2.2/lib/grape/middleware/formatter.rb#L43"&gt;method format_from_extension&lt;/a&gt; in grape Formatter
middleware caused memory leak, it genereate a symbol no matter if there
is an extension in the request path, e.g.&lt;/p&gt;

&lt;p&gt;if requesting /v1/blog/post/1, it will create symbol :"/v1/blog/posts/1"&lt;/p&gt;

&lt;p&gt;if requesting /v1/blog/post/2, it will create symbol :"/v1/blog/posts/2"&lt;/p&gt;

&lt;p&gt;......&lt;/p&gt;

&lt;p&gt;In case you don't know, symbol won't be garbage collected in ruby, so
every time it got a request path different then before, it created a
symbol in memory which won't be garbage collected.&lt;/p&gt;

&lt;p&gt;Problem detected, solution is &lt;a href="https://github.com/intridea/grape/pull/291"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In conclusion, be careful to ruby symbol, do not convert any non
controlled string to symbol.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/h9a-3Tp5xwg" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>JRuby at OpenFeint - a JRuby migration success story</title>
   <link href="http://www.huangzhimin.com/2012/11/14/jruby-at-openfeint-jruby-migration-success-story/" />
   <updated>2012-11-14T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/11/14/jruby-at-openfeint-jruby-migration-success-story</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; OpenFeint gets 40% performance improvement after migrating
to JRuby from REE.&lt;/p&gt;

&lt;h2&gt;About OpenFeint&lt;/h2&gt;

&lt;p&gt;OpenFeint was the largest mobile social gaming platform in the world,
It was acquired by GREE for $104 million last year, and a new global
platform is building to replace OpenFeint. It is still one of the
biggest rails applications, with hundreds of thousands API calls per
minute.&lt;/p&gt;

&lt;p&gt;OpenFeint platform is using rails 2.3.14 and was running on ree 1.8.7.&lt;/p&gt;

&lt;h2&gt;Why try JRuby&lt;/h2&gt;

&lt;p&gt;My main job is to improve the performance and scalability of OpenFeint
platform.  This April, I attended Railsconf at Austin, there was
&lt;a href="http://railsconf2012.com/sessions/66"&gt;a panel discussion&lt;/a&gt; talking about real world rails apps, speakers
came from New Relic, Zendesk, Groupon, etc. They use the similar
achitecture like us, ree 1.8.7, rails 2.3, mysql, memcached, redis,
rabbitmq and so on. They all complained the slow gc of ruby 1.8.7, so
did we. After that, there are 2 jruby sessions interested me.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://railsconf2012.com/sessions/62"&gt;Up and to the right - how Spiceworks is scaling 200 million requests
per month&lt;/a&gt;, they shown how they migrate their rails app to jruby and
got 20% performance improvement.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://railsconf2012.com/sessions/16"&gt;Complex Made Simple: Sleep Better with Torquebox&lt;/a&gt;, it introduced
torquebox, a ruby application server that is build on JRuby and JBoss
AS 7.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;When I went back to hotel, I googled something about jruby performance
and found &lt;a href="http://torquebox.org/news/2011/10/06/torquebox-2x-performance/"&gt;torquebox performance benchmark&lt;/a&gt;, it looked pretty
exciting. At that time I decided to try jruby on OpenFeint platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; you probably know new relic and zendesk have already migrated
to ruby 1.9.&lt;/p&gt;

&lt;h2&gt;Quick and dirty performance test with JRuby&lt;/h2&gt;

&lt;p&gt;I always prefer doing the performance test by myself rather than blindly
believing the performance benchmark online. So the first thing I want to
do was to do a quick performance test with JRuby on OpenFeint platform.&lt;/p&gt;

&lt;p&gt;It was expected that OpenFeint platform couldn't work with JRuby.  To
quickly verfiy if JRuby could give us a great performance improvement, I
fixed incompatible ruby gems, like adding jruby-openssl gem, removing
SystemTimer gems and using activerecord-jdbcmysql-adapter instead of
mysql gem. I also did some dirty hacks, e.g. I disabled database
sharding, background job and other non working parts, just want to do
a quick performance test. Then I deployed app to one of our qa servers,
the result of quick performance test is as follows&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;response time of ree + passenger is &lt;strong&gt;331ms&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;response time of jruby + torquebox is &lt;strong&gt;51.5ms&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I was shocked that JRuby is so fast, that made it easy to persuade
manager to migrate OpenFeint platform to JRuby.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; our qa environment is quite different to production
environment, databases are shared between qa servers, but memcached,
redis, rabbitmq and app server are working together in one host, and
ree on qa server didn't do any gc tuning.&lt;/p&gt;

&lt;h2&gt;JRuby migration strategy&lt;/h2&gt;

&lt;p&gt;After the quick performance test, JRuby looked very promising, then I'm
allowed to focus my work on JRuby migration. Before I tell you how we
migrate to JRuby, please let me give you a short introduction about
what OpenFeint platform uses&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;load balancer servers with nginxes.&lt;/li&gt;
&lt;li&gt;app servers with nginx + passenger.&lt;/li&gt;
&lt;li&gt;memcached servers for caches.&lt;/li&gt;
&lt;li&gt;redis servers for feature flags, high score caches, device mapper, etc.&lt;/li&gt;
&lt;li&gt;mysql servers for data storage.&lt;/li&gt;
&lt;li&gt;uses rabbitmq server and workling servers to handle background jobs.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Of course OpenFeint platform uses other servers for cron job, performance
test, continuous integration, full text search, log analytics, etc.&lt;/p&gt;

&lt;p&gt;To handle the massive requests, OpenFeint platform splits app and
databse servers into different pools according to different
functionalities.&lt;/p&gt;

&lt;p&gt;Each app pool is isolated, they don't know each other. Load balancer
servers decide sending requests to which pool according to the request
urls. Each pool will connect to all db servers, e.g. high score app
servers will fetch high score info from high score dbs and fetch
user/game info from core dbs.&lt;/p&gt;

&lt;p&gt;Considering that we don't have experienced java ops and we only have
1 or 2 qas can involve in, it is a big risk to migrate the whole
OpenFeint platform to JRuby. So I decide to do JRuby migrate one app pool
by one app pool.&lt;/p&gt;

&lt;p&gt;The advantage of migration one pool by one pool is it allows OpenFeint
gets the JRuby's speed earlier, 1 or 2 qas are enough to promise app is
working correctly for one pool, ops can setup jruby environment and tune
the jvm performance on one pool's hosts to accumulate jruby experience.&lt;/p&gt;

&lt;p&gt;The disadvantage is we have to promise OpenFeint platform is working
well on both REE and JRuby, running app with REE on some pools and
running app with JRuby on other pools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; only load balancers and mysql servers are dedicated servers,
others are VPS.&lt;/p&gt;

&lt;h2&gt;Fix incompatible gems&lt;/h2&gt;

&lt;p&gt;The most problems for migrating a rails app to JRuby are incompatible
gems, like c extensions gems or some non thread-safe ruby gems. I
encountered 2 incompatible gems that wasted my time.&lt;/p&gt;

&lt;p&gt;1. &lt;a href="https://github.com/typhoeus/typhoeus"&gt;typhoeus&lt;/a&gt;, it is one of the fastest http client ruby gems, it's
a c extenion gem, we used it to synchronize data between OpenFeint
platform and the new global platform. The official document says it is
built with FFI and is ready for use with any Ruby implementation. But
during performance test, I found it always crashed the JVM after running
about 1 hour. According to the crash log, I fixed a missing
attach_function &lt;a href="https://github.com/aurorafeint/typhoeus/commit/3ed63661e4f8e6b12e340fe3e84ba3a80b5c6f26"&gt;here&lt;/a&gt;, but it didn't help. I ended up using
net-http-persistent in JRuby while using typhoeus in REE. From
performance test, I surprisingly found JRuby + net-http-persistent isn't
slower than REE + JRbuy.&lt;/p&gt;

&lt;p&gt;2. &lt;a href="https://github.com/evan/memcached"&gt;memcached&lt;/a&gt;, it is the fastest memcached client ruby gems, it's
also a c extension gem. At first I used &lt;a href="https://github.com/ikai/jruby-memcache-client"&gt;jruby-memcache-client&lt;/a&gt;, but
jruby-memcache-client uses Base64 to encode/decode value, which can't
work with memcached gem together. Then I chose &lt;a href="https://github.com/mperham/dalli"&gt;dalli&lt;/a&gt; which supports
both REE and JRuby, but it uses different hash and distribution
algorithms, which causes too much cache misses on production. I searched
some other jruby memcached clients, but none of them are compatible with
memcached gem, I ended up writing &lt;a href="https://github.com/aurorafeint/jruby-memcached"&gt;jruby-memcached gem&lt;/a&gt; by myself
based on spymemcached. I wrote a post about this gem before, check it
out &lt;a href="http://huangzhimin.com/2012/07/24/jruby-memcached-0-1-0-released/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Enable threadsafe&lt;/h2&gt;

&lt;p&gt;By default, threadsafe is disabled in rails 2.3.14, which means every
requests are locked by Rack::Lock, it's not a big deal when running in
multi-processes servers, like unicorn or passenger, but it loses the
JRuby's natvie multi-threads power. So make sure you enable the
threadsafe when migrating to JRuby.&lt;/p&gt;

&lt;p&gt;Enabling threadsafe means rails won't automatically load libraries under
lib/ directory, you have to load them by yourselves.&lt;/p&gt;

&lt;p&gt;Enabling threadsafe also means you must consider thread safety seriously.
OpenFeint platform uses long-running threads to communicate with scribe,
there is a eager loaded global queue and a lazy loaded thread for each
process, when doing performance test with JRuby + Torquebox, sometimes it
will genereate several lazy loaded threads, and finally cause memory
leak. The solution is to eager load the long running thread.&lt;/p&gt;

&lt;h2&gt;Pass all tests&lt;/h2&gt;

&lt;p&gt;It's a common sense that you must have good coverage unit, functional
and integration tests before doing a big migration. When all tests
were passed, I was confident to go further.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; JRuby always eat much more memory to run memory, for openfeint
platform, I have to allocate 2 GB memory&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;JRUBY_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-J-Xmx2g jruby --client -S bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rake &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Pick up a JRuby server&lt;/h2&gt;

&lt;p&gt;There are 4 JRuby servers that I can choose&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/trinidad/trinidad"&gt;Trinidad&lt;/a&gt;, built on JRuby::Rack and Tomcat.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/torquebox/torquebox"&gt;Torquebox&lt;/a&gt;, built on JBoss AS.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/matadon/mizuno"&gt;Mizuno&lt;/a&gt;, built on Jetty.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puma/puma"&gt;Puma&lt;/a&gt;, a new ruby web server built for concurrency.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Puma depends on rack ~&gt; 1.2 but rails 2.3.14 depends on ~&gt; 1.1.0, so I
can't try Puma for OpenFeint platform.&lt;/p&gt;

&lt;p&gt;I chose Torquebox from the other 3 servers, the reasons are as follows.&lt;/p&gt;

&lt;p&gt;1. Torquebox runs faster than Trinidad and Mizuno according to our own
performance test, I think this is bacause Torquebox is mostly written
by Java while other servers are written by Ruby.
2. Some Torquebox core team members are paid by Red Hat to work on
Torquebox project, that means we can get better supports.
3. Torquebox project is very actively developing, and always keeps up
with latest JBoss AS server and JRuby.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Recently I replaced torquebox with &lt;a href="https://github.com/torquebox/torquebox-lite"&gt;torquebox-lite&lt;/a&gt;, which
is a smaller, web-only version of Torquebox, you can easily add other
jboss submodules when necessary.&lt;/p&gt;

&lt;h2&gt;Monitor JVM&lt;/h2&gt;

&lt;p&gt;Running on JVM is quite different than running on REE, you probably face
some new issues, like memory leak and thread safety. We uses
&lt;a href="http://newrelic.com/"&gt;New Relic&lt;/a&gt; to monitor response time, throughput, etc., but it
doesn't help to monitor jvm heap / non heap memory and thread stacks.
Fortunately we also use &lt;a href="http://scoutapp.com/"&gt;scout&lt;/a&gt; to monitor our servers, scout
provides JMX Monitoring plugin which collects the memory usage of jvm.
It is okay for production so far, but we will use &lt;a href="http://www.zabbix.com/"&gt;zabbix&lt;/a&gt; for
better monitoring in the future.&lt;/p&gt;

&lt;p&gt;In Java world, there are a lot of monitor tools. Command tools like
jstat, jstack and jmap, graphical tools like jconsole and visualvm, you
can easily get the heap / non heap memory usage, gc stats, each thead
stack trace, etc.&lt;/p&gt;

&lt;p&gt;It's really important to monitor JVM when doing performance / stress test,
it can help you find out memory leak and thread safe issues before
running on production. Here are 2 examples.&lt;/p&gt;

&lt;p&gt;1. memory leak, I noticed that heap memory (both edge and old) reached
100% during stress test. Although no OutOfMemoryError raised, it was
definitely a memory leak. I used jmap to dump all heap memory and read
them by &lt;a href="http://www.eclipse.org/mat/"&gt;Eclipse MAT&lt;/a&gt;, here is the result.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://farm9.staticflickr.com/8196/8134695856_e06ba13e7f.jpg" alt="memory leak in eclipse mat" /&gt;&lt;/p&gt;

&lt;p&gt;It's a typical memory leak, objects in container can't be gabarge
collected.&lt;/p&gt;

&lt;p&gt;2. thread safe, I also found the db connection pool in activerecord
2.3.14 is not thread safe. The throughput will decline after running
a long time, I used jstack to dump all threads stack trace and saw
most of threads are locked in connection_pool as follows.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="s2"&gt;&amp;quot;http--127.0.0.1-8180-1&amp;quot;&lt;/span&gt; daemon &lt;span class="nv"&gt;prio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10 &lt;span class="nv"&gt;tid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x00007f4a17609800 &lt;span class="nv"&gt;nid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x725a in Object.wait&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;0x0000000049dfc000&lt;span class="o"&gt;]&lt;/span&gt;
   java.lang.Thread.State: TIMED_WAITING &lt;span class="o"&gt;(&lt;/span&gt;on object monitor&lt;span class="o"&gt;)&lt;/span&gt;
  at java.lang.Object.wait&lt;span class="o"&gt;(&lt;/span&gt;Native Method&lt;span class="o"&gt;)&lt;/span&gt;
  - waiting on &amp;lt;0x0000000704f40e18&amp;gt; &lt;span class="o"&gt;(&lt;/span&gt;a org.jruby.libraries.ThreadLibrary&lt;span class="nv"&gt;$ConditionVariable&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  ......
  at rubyjit.ActiveRecord::ConnectionAdapters::ConnectionPool#checkout_0978F3C1EFB2CBFA2CD717B12DA76E3113CD78B7.block_1&lt;span class="nv"&gt;$RUBY$__file__&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;/home/deploy/rails_apps/  openfeint_platform/shared/bundle/jruby/1.8/gems/activerecord-2.3.14/lib/active_record/connection_adapters/abstract/connection_pool.rb:192&lt;span class="o"&gt;)&lt;/span&gt;
  at rubyjit&lt;span class="nv"&gt;$ActiveRecord&lt;/span&gt;::ConnectionAdapters::ConnectionPool#checkout_0978F3C1EFB2CBFA2CD717B12DA76E3113CD78B7&lt;span class="nv"&gt;$block_1$RUBY$__file__&lt;/span&gt;.                          call&lt;span class="o"&gt;(&lt;/span&gt;rubyjit&lt;span class="nv"&gt;$ActiveRecord&lt;/span&gt;::ConnectionAdapters::ConnectionPool#checkout_0978F3C1EFB2CBFA2CD717B12DA76E3113CD78B7&lt;span class="nv"&gt;$block_1$RUBY$__file__&lt;/span&gt;:65535&lt;span class="o"&gt;)&lt;/span&gt;
  ......
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;But the count of http threads is equal to the count of db connections,
no thread should be locked. Considering our situation, I added a monkey
patch to connection_pool with one db connection per thread. It's not
perfect but works well.&lt;/p&gt;

&lt;h2&gt;Tune JVM performance&lt;/h2&gt;

&lt;p&gt;There are several jvm settings you should set for JRuby performance.&lt;/p&gt;

&lt;p&gt;1. Xms and Xmx, when we hot deployed app to Torquebox by touching
-knob.yml.dodeploy, it took more than 20 minutes to complete, which was
unacceptable, after discussing with Torqeubox support team, I knew
default value for Xms is 64m and Xmx is 256m, they are too small, then I
increased them to 2g, it took only 100 seconds to hot deploy. The root
cause is hot deployment will increase memory a lot, which causing lots
of full GCs.&lt;/p&gt;

&lt;p&gt;2. CodeCache, when we do the performance test, I found response time
suddenly jumped after running a few minutes, the torquebox log told me
"CodeCache is full. Compiler has been disabled." CodeCache is a part of
non heap memory in Hopspot JVM, it's 64m by default, so I increased it
to 256m by setting -XX:ReservedCodeCacheSize=256m, then I don't see
the response time jump anymore.&lt;/p&gt;

&lt;p&gt;There are a lot of JVM parameters you can tune for your application,
talk and learn from some Java experts.&lt;/p&gt;

&lt;h2&gt;Performance / Stress test&lt;/h2&gt;

&lt;p&gt;I mentioned I already did a quick performance test, but it didn't make a
big sense, because qa and production have different environments. So
this time I did performance / stress tests on a reserved host, which has
the exactly same environments with production servers, connecting to
production database, memcache and redis servers.&lt;/p&gt;

&lt;p&gt;Here are test results for actions in one pool.&lt;/p&gt;

&lt;table style="margin-bottom: 18px"&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&amp;nbsp;read action&amp;nbsp;&amp;nbsp;&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&amp;nbsp;write action&amp;nbsp;&amp;nbsp;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;REE 1.8.7 + passenger&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;448 ms&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;44 ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Ruby 1.9.3 + passenger&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;374 ms&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;42 ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;JRuby 1.7.0.RC2 + torquebox-lite  &lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;187 ms&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;38 ms&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;


&lt;p&gt;JRuby is much faster than REE 1.8.7 and Ruby 1.9.3 in both read and
write actions. It's promising we can get a big performance improvement
on production.&lt;/p&gt;

&lt;p&gt;Make sure you run your stress tests multiple times and run long time,
some memory leak and thread safety issues are not reproduced every time
or not occurred in a short time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; REE in reserved host is already optimized with &lt;a href="https://gist.github.com/841168"&gt;twitter's
settings&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Deployment strategy&lt;/h2&gt;

&lt;p&gt;Everything was ready, it was time to think about deployment strategy.&lt;/p&gt;

&lt;p&gt;In Java world, you can deploy an app by packaging your source code into
a war file and copying the war package to app server. We can do the same
thing with JRuby, but it will break our existing capistrano deployment
script.&lt;/p&gt;

&lt;p&gt;We kept existing capistrano deployment script except deploy:restart
task, replacing&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;touch tmp/restart.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;with&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;touch /opt/torquebox/current/jboss/standalone/deployments/openfeint_platform-knob.yml.dodeploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Torquebox will detect openfeint_platform-knob.yml.dodeploy, undeploy
old openfeint_platform and deploy new openfeint_platform, works very
similar to passenger. But I found everytime we redeploy app, the non
heap memory will jump a lot and the app will be super slow (multiple
times slower than usual) during redeployment process.&lt;/p&gt;

&lt;p&gt;So I decided to deploy app by restarting jboss instead of hot
deployment.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;sudo /etc/init.d/jboss-as-standalone restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It solved memory issue, mitigated the slow requests, but introduced a
new issue, it will lost the requests during restarting jboss. The
solution we used is rolling restart to provide zero downtime
deployment, e.g. we have 3 app servers A, B, C&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;tell load balancers stop sending http requests to server A.&lt;/li&gt;
&lt;li&gt;restart jboss on server A.&lt;/li&gt;
&lt;li&gt;tell load balancers resend http resquests to server A when jboss
on server A is ready.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;And restart server B and C one by one following the above steps.&lt;/p&gt;

&lt;p&gt;So far, it works perfect, no memory jump and no request lost.&lt;/p&gt;

&lt;h2&gt;JRuby on production&lt;/h2&gt;

&lt;p&gt;Finally we successfully migrated to JRuby on production and the response
time dropped a lot.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://farm9.staticflickr.com/8328/8130182602_8106be24de.jpg" alt="performance improvement with JRuby" /&gt;&lt;/p&gt;

&lt;p&gt;It was about 40% performance improvement, although it was expected, I
was still very excited. Actually after fully warming up, it run even
faster than you see on the figure.&lt;/p&gt;

&lt;p&gt;The following is the response time comparing to ree's 1 week ago.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://farm9.staticflickr.com/8046/8134823364_f065545213.jpg" alt="jruby performance comparison" /&gt;&lt;/p&gt;

&lt;p&gt;This is the successful migration for one pool on OpenFeint platform, we
have already migrated 5 pools to JRuby, all got ~ 40% performance
improvement. I'm still working on the rest pools' migration and looking
forward to replacing all OpenFeint servers to JRuby.&lt;/p&gt;

&lt;p&gt;Some JRuby servers have been running on OpenFeint platform for more than
2 months, they are running stably and much faster than before according
to New Relic's weekly report.&lt;/p&gt;

&lt;h2&gt;Further&lt;/h2&gt;

&lt;p&gt;Java 7 introduced invokedynamic feature, a lot of people said
enabling invokedynamic made JRuby 1.7 run much faster, closer to Java
speed. But I'm failed tn enable invokedynamic feature with Torquebox,
saw the following error&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;18:29:03,515 ERROR &lt;span class="o"&gt;[&lt;/span&gt;org.torquebox.core.runtime&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Thread-71&lt;span class="o"&gt;)&lt;/span&gt; Error during execution: ENV&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;RAILS_ROOT&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;RACK_ROOT
ENV&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;RAILS_ENV&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;RACK_ENV
require %q&lt;span class="o"&gt;(&lt;/span&gt;org/torquebox/web/rails/boot&lt;span class="o"&gt;)&lt;/span&gt;
: org.jruby.exceptions.RaiseException: &lt;span class="o"&gt;(&lt;/span&gt;LoadError&lt;span class="o"&gt;)&lt;/span&gt; load error: haml/buffer -- java.lang.NoClassDefFoundError: org/jruby/runtime/ThreadContext
     at org.jruby.RubyKernel.require&lt;span class="o"&gt;(&lt;/span&gt;org/jruby/RubyKernel.java:1010&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;jruby.jar:&lt;span class="o"&gt;]&lt;/span&gt;
     at ActiveSupport::Dependencies::Loadable.require&lt;span class="o"&gt;(&lt;/span&gt;/home/deploy/rails_apps/openfeint_platform/shared/bundle/jruby/1.8/gems/activesupport-2.3.14/lib/active_support/dependencies.rb:182&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Torquebox team is trying to fix this issue, I will definitely enable
invokedynamic with new Torquebox release, and am looking forward to
another big performance improvement.&lt;/p&gt;

&lt;h2&gt;Some Resources&lt;/h2&gt;

&lt;p&gt;If you join the JRuby world, the first thing you need to do is to follow
&lt;a href="https://twitter.com/headius"&gt;Charles Nutter&lt;/a&gt; on twitter, he is one of the JRuby core team
members and always shares a lot of JRuby knowledge. Also check out his
&lt;a href="https://speakerdeck.com/headius"&gt;presentations&lt;/a&gt; to get latest JRuby features and benchmarks.
&lt;a href="https://github.com/jruby/jruby/wiki/_pages"&gt;JRuby wiki pages&lt;/a&gt; are helpful to learn everything about JRuby.&lt;/p&gt;

&lt;p&gt;At the end, please allow me to thank JRuby and Torquebox team for
providing such great things and thank &lt;a href="http://product.gree.net/us/en"&gt;Gree&lt;/a&gt; for allowing me to share
the knowledge.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/ZoMaAjXYbdA" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>switch_user 0.9 released</title>
   <link href="http://www.huangzhimin.com/2012/10/31/switch_user-0.9-released/" />
   <updated>2012-10-31T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/10/31/switch_user-0.9-released</id>
   <content type="html">&lt;p&gt;&lt;a href="https://github.com/flyerhzm/switch_user"&gt;switch_user&lt;/a&gt; provides a convenient way to switch current user that
speeds up your development and reproduce user specified error on
production.&lt;/p&gt;

&lt;p&gt;Today switch_user gem 0.9.0 is released, all thanks to &lt;a href="https://github.com/lcowell"&gt;Luke Cowell&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;He is a collaborator of switch_user gem, and did a great job for 0.9.0
gem.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;did lots of refactors.&lt;/li&gt;
&lt;li&gt;added unit tests.&lt;/li&gt;
&lt;li&gt;made switch_user a rails engine.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;check out the changelog &lt;a href="https://github.com/flyerhzm/switch_user/compare/v0.8.0...v0.9.0"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also updated &lt;a href="https://github.com/flyerhzm/switch_user_example"&gt;switch_user example&lt;/a&gt; to use switch_user 0.9.0.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/RMxWLHZkLME" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>zero downtime deployment</title>
   <link href="http://www.huangzhimin.com/2012/10/23/zero-downtime-deployment/" />
   <updated>2012-10-23T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/10/23/zero-downtime-deployment</id>
   <content type="html">&lt;p&gt;This is my new post on jrubytips. It teaches you how to achieve zero
downtime deployment for jruby servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://jrubytips.com/posts/5-zero-downtime-deployment"&gt;http://jrubytips.com/posts/5-zero-downtime-deployment&lt;/a&gt;&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/8tQkl-fXRlE" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>set proper value for CodeCache</title>
   <link href="http://www.huangzhimin.com/2012/10/19/set-proper-value-for-codecache/" />
   <updated>2012-10-19T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/10/19/set-proper-value-for-codecache</id>
   <content type="html">&lt;p&gt;This is my new post on jrubytips. It tells you the jvm CodeCache which
may affect your server performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://jrubytips.com/posts/4-set-proper-value-for-codecache"&gt;http://jrubytips.com/posts/4-set-proper-value-for-codecache&lt;/a&gt;&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/XRsmdisw8A8" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>newrelic-rake released</title>
   <link href="http://www.huangzhimin.com/2012/10/12/newrelic-rake-released/" />
   <updated>2012-10-12T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/10/12/newrelic-rake-released</id>
   <content type="html">&lt;p&gt;4 months ago, I released &lt;a href="https://github.com/aurorafeint/newrelic-workling"&gt;newrelic-workling&lt;/a&gt; gem, which helps us
montior the performance of background jobs. We used it to find out a
GC performance issue. But we still have some cron jobs, who call rake
tasks, running in the black box.&lt;/p&gt;

&lt;p&gt;So I created a new project &lt;a href="https://github.com/flyerhzm/newrelic-rake"&gt;newrelic-rake&lt;/a&gt; that adds newrelic
instrument for rake tasks. Now when I go to the newrelic, I can see the
rake tasks listed in Background tasks section, it shows me the average
execution time and call count for all rake tasks.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://farm9.staticflickr.com/8467/8078580542_a85b59f8bd.jpg" alt="newrelic rake tasks" /&gt;&lt;/p&gt;

&lt;p&gt;I can also see the performance breakdown for each rake task.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://farm9.staticflickr.com/8475/8078589421_7d3aa63972.jpg" alt="newrelic rake instrument" /&gt;&lt;/p&gt;

&lt;p&gt;This rake task probably needs to use persistence net http or some c
extension http client, and reduce the GC calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's really important to do monitor first, then do optimize.&lt;/strong&gt;&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/dPYhxHgzvy0" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>avoid using rubyzip</title>
   <link href="http://www.huangzhimin.com/2012/10/02/avoid-using-rubyzip/" />
   <updated>2012-10-02T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/10/02/avoid-using-rubyzip</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;More precisely I want to say allocating as less objects as you can,
rubyzip is just an example.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have a background job compressing webui assets, uploading to S3, so
mobile sdk can download assets to update webui dynamically.&lt;/p&gt;

&lt;p&gt;After iphone5 and ios6 came to the market, we received much more webui
requests than before, it was expected, but our background job couldn't
consume so much asynchronous messages. We could easily scale out by adding
more background job servers, but I decided diving deeply into webui job to
see if I could speed it up and increase throughput.&lt;/p&gt;

&lt;p&gt;Thank newrelic for providing great monitoring service, I saw the webui
job took averagely 725ms to complete a webui job, and 80% time was taken
by GC calls, WTF. Instead of blaming ruby gc, I blamed our bad code.&lt;/p&gt;

&lt;p&gt;I noticed that we used &lt;a href="https://rubygems.org/gems/rubyzip"&gt;rubyzip&lt;/a&gt; to compress webui assets, it was the
root reason to cause so much GC.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="ss"&gt;Zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:ZipFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;Zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:ZipFile&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CREATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;source_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/public/webui/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="n"&gt;expand_dirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="k"&gt;begin&lt;/span&gt;
          &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;
        &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="ss"&gt;Errno&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:EEXIST&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_path&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It sucks, all files are reading and compressing in ruby VM, too many
objects are allocated, then cause several GC calls. So I tried to use
shell zip command instead of rubyzip.&lt;/p&gt;

&lt;p&gt;I did an experiment between rubyzip and shell zip. The followings are
code examples.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;zip/zip&amp;#39;&lt;/span&gt;
&lt;span class="ss"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Profiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;
&lt;span class="n"&gt;before_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ObjectSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count_objects&lt;/span&gt;
&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
&lt;span class="ss"&gt;Zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:ZipFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;test.zip&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;Zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:ZipFile&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CREATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/*&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Total time: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;after_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ObjectSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count_objects&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;[GC Stats] &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;before_stats&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:FREE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;after_stats&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:FREE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; new allocated objects.&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Total time: 0.75344&lt;/span&gt;
&lt;span class="c1"&gt;# [GC Stats] 718691 new allocated objects.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;




&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="ss"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Profiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;
&lt;span class="n"&gt;before_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ObjectSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count_objects&lt;/span&gt;
&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/*&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;directory?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="sb"&gt;`zip test.zip &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Total time: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;after_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ObjectSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count_objects&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;[GC Stats] &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;before_stats&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:FREE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;after_stats&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:FREE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; new&lt;/span&gt;
&lt;span class="s2"&gt;allocated objects.&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Total time: 0.349816&lt;/span&gt;
&lt;span class="c1"&gt;# [GC Stats] 2269 new allocated objects.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As you can see, rubyzip allocates &gt; 700k objects for reading and
compressing, and it also takes more than double time to finish the
script, shell zip command is a much better solution. So I replaced
rubyzip with shell zip in our product.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="sb"&gt;`cd &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;/public/webui &amp;amp;&amp;amp; zip &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;After deploying to background job server, I see a big performance
improved, it takes only 218ms for webui job to finish, and only 28%
time is taken by GC calls. The throughput is also increased from 44cpm
to 64cpm, and it can keep up with the webui asyncrhonous messages, we
don't need to add more servers, money saved. :-)&lt;/p&gt;

&lt;p&gt;So keep in mind, allocating less objects means less GC calls, also means
better performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updated&lt;/strong&gt;: &lt;a href="https://rubygems.org/gems/zipruby"&gt;zip_ruby&lt;/a&gt; gem gives a similar speed of shell zip command.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;zipruby&amp;#39;&lt;/span&gt;
&lt;span class="ss"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Profiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;
&lt;span class="n"&gt;before_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ObjectSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count_objects&lt;/span&gt;
&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
&lt;span class="ss"&gt;Zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Archive&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;test.zip&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;Zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:CREATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/*&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_file&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Total time: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;after_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ObjectSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count_objects&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;[GC Stats] &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;before_stats&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:FREE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;after_stats&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:FREE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; new allocated objects.&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Total time: 0.367729&lt;/span&gt;
&lt;span class="c1"&gt;# [GC Stats] 1116 new allocated objects.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/d9aQOB7TkIM" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>speed up git deployment with depth 1</title>
   <link href="http://www.huangzhimin.com/2012/09/14/speed-up-git-deployment-with-git_shallow_clone/" />
   <updated>2012-09-14T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/09/14/speed-up-git-deployment-with-git_shallow_clone</id>
   <content type="html">&lt;p&gt;By default, when you deploy your application by capistrano git, it will
clone the repository with entire history on production server, but it's
meaningless. You should never go to production host and check git log,
instead you just need latest code on production host.&lt;/p&gt;

&lt;p&gt;With your application grows, git clone with entire history may take a
bit longer time than you expected. The following is the time spent with
fully cloning.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;git clone git@github.com:railsbp/rails-bestpractices.com.git
Cloning into &lt;span class="s1"&gt;&amp;#39;rails-bestpractices.com&amp;#39;&lt;/span&gt;...
remote: Counting objects: 11438, &lt;span class="k"&gt;done&lt;/span&gt;.
remote: Compressing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;3915/3915&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;.
remote: Total 11438 &lt;span class="o"&gt;(&lt;/span&gt;delta 7012&lt;span class="o"&gt;)&lt;/span&gt;, reused 11277 &lt;span class="o"&gt;(&lt;/span&gt;delta 6886&lt;span class="o"&gt;)&lt;/span&gt;
Receiving objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;11438/11438&lt;span class="o"&gt;)&lt;/span&gt;, 5.52 MiB | 127 KiB/s, &lt;span class="k"&gt;done&lt;/span&gt;.
Resolving deltas: 100% &lt;span class="o"&gt;(&lt;/span&gt;7012/7012&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;.
git clone git@github.com:railsbp/rails-bestpractices.com.git  0.55s user 0.26s system 1% cpu 55.275 total
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;But if clone with depth 1, it's finished much faster since there is only
1 revision fetched.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;git clone --depth 1 git@github.com:railsbp/rails-bestpractices.com.git
Cloning into &lt;span class="s1"&gt;&amp;#39;rails-bestpractices.com&amp;#39;&lt;/span&gt;...
remote: Counting objects: 1635, &lt;span class="k"&gt;done&lt;/span&gt;.
remote: Compressing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;1243/1243&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;.
remote: Total 1635 &lt;span class="o"&gt;(&lt;/span&gt;delta 265&lt;span class="o"&gt;)&lt;/span&gt;, reused 1367 &lt;span class="o"&gt;(&lt;/span&gt;delta 189&lt;span class="o"&gt;)&lt;/span&gt;
Receiving objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;1635/1635&lt;span class="o"&gt;)&lt;/span&gt;, 3.02 MiB | 134 KiB/s, &lt;span class="k"&gt;done&lt;/span&gt;.
Resolving deltas: 100% &lt;span class="o"&gt;(&lt;/span&gt;265/265&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;.
git clone --depth 1 git@github.com:railsbp/rails-bestpractices.com.git  0.24s user 0.17s system 1% cpu 34.236 total
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It's time to apply this on your capistrano file to speed up your
deployment.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:scm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:git&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:git_shallow_clone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: git_shallow_clone can't work with branch.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/oliuOSLmor4" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>enable threadsafe for rails</title>
   <link href="http://www.huangzhimin.com/2012/09/09/enable-threadsafe-for-rails/" />
   <updated>2012-09-09T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/09/09/enable-threadsafe-for-rails</id>
   <content type="html">&lt;p&gt;This is my new post on jrubytips. It tells what threadsafe is in rails,
and what're the benefits with threadsafe in jruby server.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://jrubytips.com/posts/3-enable-threadsafe-for-rails"&gt;http://jrubytips.com/posts/3-enable-threadsafe-for-rails&lt;/a&gt;&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/rDg76Tejsn8" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>rolling out with feature flags</title>
   <link href="http://www.huangzhimin.com/2012/09/02/rolling-out-with-feature-flags/" />
   <updated>2012-09-02T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/09/02/rolling-out-with-feature-flags</id>
   <content type="html">&lt;p&gt;This is my new post on rails-bestpractices. It tells how we use feature
flags to rolling out our features.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://rails-bestpractices.com/posts/697-rolling-out-with-feature-flags"&gt;http://rails-bestpractices.com/posts/697-rolling-out-with-feature-flags&lt;/a&gt;&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/1VctwE5HSFY" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>how to write a jruby gem - part 2</title>
   <link href="http://www.huangzhimin.com/2012/08/23/how-to-write-a-jruby-gem-part-2/" />
   <updated>2012-08-23T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/08/23/how-to-write-a-jruby-gem-part-2</id>
   <content type="html">&lt;p&gt;In my &lt;a href="http://huangzhimin.com/2012/08/06/how-to-write-a-jruby-gem-part-1/"&gt;previous post&lt;/a&gt;, I introduced how to write a jruby gem with
ruby code, today I will show you how to write a jruby extension with
java code, which can give you better performance.&lt;/p&gt;

&lt;h3&gt;Standard Steps&lt;/h3&gt;

&lt;p&gt;1. create java classes to wrap any java library you need, and the java
classes must extend RubyObject, then it can be called from jruby. e.g.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Memcached&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;RubyObject&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// MemcachedClient is what we want to wrap&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;MemcachedClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// java constructor&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Memcached&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Ruby&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RubyClass&lt;/span&gt; &lt;span class="n"&gt;rubyClass&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rubyClass&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ruby initialize&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ThreadContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MemcachedClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;new&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// wrapper method, the first argument for jruby methods must be ThreadContext&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ThreadContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IRubyObject&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Keep in mind, every objects you read from ruby or return to ruby must be
a RubyObject. So you have to convert between RubyObject and java Object in
your wrapper methods.&lt;/p&gt;

&lt;p&gt;2. add JRubyModule, JRubyClass, JRubyMethod and JRubyConstant annotations.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@JRubyClass&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Memcached&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;RubyObject&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@JRubyMethod&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ThreadContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MemcachedClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;new&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@JRubyMethod&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ThreadContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IRubyObject&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;JRuby annotations tells jvm which classes and methods should be open to
ruby world. It can tell the details of classes and methods, like
what's the parent class, how many arguments of a methods, and so on.&lt;/p&gt;

&lt;p&gt;3. load all jruby modules, classes and methods with BasicLibraryService.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemcachedService&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;BasicLibraryService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;basicLoad&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Ruby&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="n"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// define Memcached class&lt;/span&gt;
        &lt;span class="n"&gt;RubyClass&lt;/span&gt; &lt;span class="n"&gt;memcached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defineClass&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Memcached&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getObject&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ObjectAllocator&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="nf"&gt;allocate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ruby&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RubyClass&lt;/span&gt; &lt;span class="n"&gt;klazz&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Memcached&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;klazz&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
        &lt;span class="c1"&gt;// define all methods with @JRubyMethods in Memcached class&lt;/span&gt;
        &lt;span class="n"&gt;memcached&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defineAnnotatedMethods&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Memcached&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;BasicLibraryService is the standard load mechanism for easy extensions,
you should implement basicLoad method to define ruby modules, classes
and methods.&lt;/p&gt;

&lt;p&gt;4. finally, load MemcachedService in your ruby file&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# MemcachedService is in com.openfeint.memcached package&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;com/openfeint/memcached/memcached&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then you can load your jruby gem, and use any Memcached classes and
methods you defined.&lt;/p&gt;

&lt;h3&gt;Some Advanced Tips:&lt;/h3&gt;

&lt;p&gt;1. JRuby method names.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;different name&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;in ruby&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;active?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;in java&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@JRubyMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;active?&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="nf"&gt;active_p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ThreadContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;alias methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;in ruby&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="ss"&gt;:&amp;quot;[]&amp;quot;&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;in java&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@JRubyMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;get&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;[]&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ThreadContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;2. JRuby method arguments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;rest arguments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;in ruby&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;in java&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@JRubyMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;initialize&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ThreadContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;arguments with default value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;in ruby&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;marshal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;in java&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@JRubyMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;get&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ThreadContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IRubyObject&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Ruby&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRuntime&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;RubyString&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RubyString&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;RubyBoolean&lt;/span&gt; &lt;span class="n"&gt;marshal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTrue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;marshal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;3. custom exceptions&lt;/p&gt;

&lt;p&gt;Exception is also a class, so, you could define an Exception in jruby
just like defining a class.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@JRubyClass&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Memcached::Error&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;RuntimeError&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// you should wrap your custom exception with RaiseException for java land throwing purpose.&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;RaiseException&lt;/span&gt; &lt;span class="nf"&gt;newNotFound&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ruby&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;RubyClass&lt;/span&gt; &lt;span class="n"&gt;errorClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getModule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Memcached&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;NotFound&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RaiseException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RubyException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorClass&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Yes, it is a subclass.&lt;/span&gt;
&lt;span class="nd"&gt;@JRubyClass&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Memcached::NotFound&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Memcached::Error&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotFound&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Finally, load the Error in MemcachedService.&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemcachedService&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;BasicLibraryService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;basicLoad&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Ruby&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="n"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;RubyClass&lt;/span&gt; &lt;span class="n"&gt;runtimeError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRuntimeError&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;RubyClass&lt;/span&gt; &lt;span class="n"&gt;memcachedError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memcached&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defineClassUnder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Error&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;runtimeError&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;runtimeError&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAllocator&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;memcached&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defineClassUnder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;NotFound&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memcachedError&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memcachedError&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAllocator&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;so when your call Error.newNotFound(ruby, "Not Found") in your java
code, it can be catched with Memcached::NotFound in ruby.&lt;/p&gt;

&lt;p&gt;4. object convertion&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RubyObject to java Object&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;you can use RubyObject convertToXXX methods&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="n"&gt;convertToArray&lt;/span&gt;
&lt;span class="n"&gt;convertToFloat&lt;/span&gt;
&lt;span class="n"&gt;convertToHash&lt;/span&gt;
&lt;span class="n"&gt;convertToInteger&lt;/span&gt;
&lt;span class="n"&gt;convertToString&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;e.g.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convertToArray&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;java Object to RubyObject&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;you can use Ruby newXXX methods&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="n"&gt;newArray&lt;/span&gt;
&lt;span class="n"&gt;newBoolean&lt;/span&gt;
&lt;span class="n"&gt;newFixnum&lt;/span&gt;
&lt;span class="n"&gt;newFloat&lt;/span&gt;
&lt;span class="n"&gt;newString&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;e.g.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You can read the source code of &lt;a href="https://github.com/aurorafeint/jruby-memcached"&gt;jruby-memcached&lt;/a&gt; to get more
information. Feel free to leave a comment if you have any question or
suggestion.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/Oufl60RFelk" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>jruby-memcached 0.5.0 released</title>
   <link href="http://www.huangzhimin.com/2012/08/22/jruby-memcached-0-5-0-released/" />
   <updated>2012-08-22T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/08/22/jruby-memcached-0-5-0-released</id>
   <content type="html">&lt;p&gt;I just released &lt;a href="https://rubygems.org/gems/jruby-memcached/versions/0.5.0"&gt;jruby-memcached 0.5.0&lt;/a&gt;, it contains the following
changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;add travis-ci support, testing jruby-18mode, jruby-19mode and
jruby-head environment.&lt;/li&gt;
&lt;li&gt;update spymemcached to 2.8.3, which set shouldOptimize to false by
default, there are some bugs with true shouldOptimize so far.&lt;/li&gt;
&lt;li&gt;fix increment/decrement issue, in &amp;lt; 0.5.0, incr/decr with unmarshal
encode while get with marshal decode.&lt;/li&gt;
&lt;li&gt;accept exception_retry_limit option.&lt;/li&gt;
&lt;li&gt;add Memcached::ATimeoutOccurred error to handle timeout case,
otherwise you will probably see following error.&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="ss"&gt;ActionView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:TemplateError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`clean_message&amp;#39; for #&amp;lt;Java::NetSpyMemcached::OperationTimeoutException:0x26e02e71&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;check out the full code changes &lt;a href="https://github.com/aurorafeint/jruby-memcached/compare/v0.4.0...v0.5.0"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/AlF9w7CCBYA" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>jruby-memcached 0.4.0 released</title>
   <link href="http://www.huangzhimin.com/2012/08/17/jruby-memcached-0-4-0-released/" />
   <updated>2012-08-17T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/08/17/jruby-memcached-0-4-0-released</id>
   <content type="html">&lt;p&gt;I just released &lt;a href="https://rubygems.org/gems/jruby-memcached/versions/0.4.0"&gt;jruby-memcached 0.4.0&lt;/a&gt;, it contains the following
changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;run spymemcached as a daemon thread. I found when running rake task
with jruby-memcached &amp;lt; 0.4.0, it won't stop unless you press Ctrl+C.&lt;/li&gt;
&lt;li&gt;get method can accept multiple keys.&lt;/li&gt;
&lt;li&gt;add Memcached::Rails as a rails cache_store. Of course, it is
compatible with Memcached::Rails in memcached.gem.&lt;/li&gt;
&lt;li&gt;make full use of jruby annotation to reduce method definitions with
optional and rest arguments.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;check out the full code changes &lt;a href="https://github.com/aurorafeint/jruby-memcached/compare/v0.3.0...v0.4.0"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/GIl5Tm74gz0" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>jruby-memcached 0.3.0 released</title>
   <link href="http://www.huangzhimin.com/2012/08/07/jruby-memcached-0-3-0-released/" />
   <updated>2012-08-07T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/08/07/jruby-memcached-0-3-0-released</id>
   <content type="html">&lt;p&gt;I just released &lt;a href="https://rubygems.org/gems/jruby-memcached/versions/0.3.0"&gt;jruby-memcached 0.3.0&lt;/a&gt;, it runs about 10%-20%
faster than 0.2.0, I removed ruby code and totally wrote it by java
code, check out the &lt;a href="https://github.com/aurorafeint/jruby-memcached/compare/v0.2.0...v0.3.0"&gt;file changes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;2 weeks ago, I released jruby-memcached 0.1.0, in &lt;a href="http://huangzhimin.com/2012/07/24/jruby-memcached-0-1-0-released/"&gt;that post&lt;/a&gt; I
mentioned jruby-memcached response time in a request is 40+ms while
memcached.gem response time is 30+ms, it looked fine, but I was
still investigating the way to improve jruby-memcached performance.&lt;/p&gt;

&lt;p&gt;After reading &lt;a href="https://github.com/headius/jruby-spymemcached"&gt;jruby-spymemcached&lt;/a&gt; gem and jruby source code, I
rewrote jruby-memcached by pure java code instead of ruby code,
because calling java from java is much faster than from ruby.&lt;/p&gt;

&lt;p&gt;I did the performance compare with new jruby-memcached, the result is
as follows:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="text"&gt;MBP 2.8G i7    jruby-memcached 0.3.0

ruby-1.9.3-p194
                              user     system      total        real
memcached set              1.110000   1.020000   2.130000 (  4.592509)
memcached get              0.970000   1.000000   1.970000 (  4.172170)
                               user     system      total        real
dalli set                  8.360000   1.650000  10.010000 ( 10.193101)
dalli get                  8.040000   1.670000   9.710000 (  9.828392)

jruby-1.6.7.2
                              user     system      total        real
jruby-memcached set       5.842000   0.000000   5.842000 (  5.842000)
jruby-memcached get       5.561000   0.000000   5.561000 (  5.561000)
                              user     system      total        real
jruby-spymemcached set    5.919000   0.000000   5.919000 (  5.919000)
jruby-spymemcached get    5.615000   0.000000   5.615000 (  5.615000)
                              user     system      total        real
dalli set                10.132000   0.000000  10.132000 ( 10.132000)
dalli get                10.600000   0.000000  10.600000 ( 10.600000)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As you can see, jruby-memcached runs as fast as jruby-spymemcached, and
it provides memcached.gem compatible apis and hashing algorithm.
jruby-memcached is still slower than memcached.gem, and on production,
the response time for memcached has reduced to 40-ms, which is very
close to the memcached.gem performance.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/Ka4Cda8VNSY" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>how to write a jruby gem - part 1</title>
   <link href="http://www.huangzhimin.com/2012/08/06/how-to-write-a-jruby-gem-part-1/" />
   <updated>2012-08-06T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/08/06/how-to-write-a-jruby-gem-part-1</id>
   <content type="html">&lt;p&gt;In my &lt;a href="http://huangzhimin.com/2012/07/24/jruby-memcached-0-1-0-released/"&gt;previous post&lt;/a&gt;, I mentioned I have written a jruby memcached
gem.  I'm glad to share my experience how to extend jruby here.&lt;/p&gt;

&lt;p&gt;JRuby is a 100% java implementation of ruby programming language, it
allows you calling java code from ruby code. Java world has much more
libraries than ruby gems, to make use of those java jar, it makes your
code easier and faster.&lt;/p&gt;

&lt;p&gt;I assume you already had the experience to create a pure ruby gem, the
first step to create a jruby gem is just the same as ruby gem, the gem
structure is as follows:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;|- lib/                   // ruby implementation code
|   |- memcached/
|   |- memcached.rb
|- &lt;span class="nb"&gt;test&lt;/span&gt;/                  // ruby &lt;span class="nb"&gt;test &lt;/span&gt;code &lt;span class="o"&gt;(&lt;/span&gt;rspec or minitest&lt;span class="o"&gt;)&lt;/span&gt;
|   |- memcached/
|   |- memcached_test.rb
|   |- test_helper.rb
|- Gemfile
|- jruby_memcached.gemspec // your gem manifest
|- Rakefile
|- README.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then let's introduce the java jar into our jruby gem.&lt;/p&gt;

&lt;p&gt;It's well-known to use maven2 to manage your java source code and
dependencies, maven uses pom.xml as a config file to define compile,
test and package processes, it also defines the dependencies, looks
like the combination of rake and bundler. All the java implementation
and test code are put in src directory, while compiled classes and jar
files are put in target directory. Now the structure looks like:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;|- lib/
|   |- memcached/
|   |- memcached.rb
|- &lt;span class="nb"&gt;test&lt;/span&gt;/
|   |- memcached/
|   |- memcached_test.rb
|   |- test_helper.rb
|- src/                    // java &lt;span class="nb"&gt;source &lt;/span&gt;code
|   |- main/               // java implementation code
|   |   |- java/
|   |- &lt;span class="nb"&gt;test&lt;/span&gt;                // java &lt;span class="nb"&gt;test &lt;/span&gt;code
|       |- java/
|- target/
|   |- classes/            // compiled classes files
|   |- spymemcached-ext-0.0.1.jar // package java &lt;span class="nb"&gt;source &lt;/span&gt;code to a jar
|- Gemfile
|- jruby_memcached.gemspec
|- pom.xml                 // maven config file, compile, &lt;span class="nb"&gt;test&lt;/span&gt;, package
|- Rakefile
|- README.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In pom.xml, I said it depends on spymemcached 2.8.1 jar, so I can import
spymemcached in my hack code under src/main/java. I also defined package
shade plugin which package spymemcached 2.8.1 jar and my hack code
together into target/spymemcached-ext-0.0.1.jar.&lt;/p&gt;

&lt;p&gt;The last step is to combine the ruby and java code. JRuby provides the
power to easily use spymemcached-ext-0.0.1.jar in ruby code.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;target/spymemcached-ext-0.0.1.jar&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;java_import&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;net.spy.memcached.MemcachedClient&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;java_import&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;net.spy.memcached.ConnectionFactoryBuilder&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;java_import&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;net.spy.memcached.AddrUtil&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ConnectionFactoryBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
&lt;span class="vi"&gt;@client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MemcachedClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;AddrUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getAddresses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As you seen, after require the java jar file, you can import the java
classes and call the java methods with ruby syntax, jruby is smart enough
to convert ruby code into java code. Check out more about how to calling
java from jruby &lt;a href="https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can check out jruby-memcached 0.2.0 source code &lt;a href="https://github.com/aurorafeint/jruby-memcached/tree/2adc85e8121229527a57a71f221fdade40de61df"&gt;here&lt;/a&gt; to get
more details. This is the simplest solution to create jruby gem, in
next post I will introduce you how to write a real jruby ext, which can
improve the performance of your jruby gem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: I have written &lt;a href="http://huangzhimin.com/2012/08/23/how-to-write-a-jruby-gem-part-2/"&gt;part 2&lt;/a&gt; for a real jruby ext, don't miss it.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/GVFJq0N5kyQ" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>jruby-memcached 0.1.0 released</title>
   <link href="http://www.huangzhimin.com/2012/07/24/jruby-memcached-0-1-0-released/" />
   <updated>2012-07-24T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/07/24/jruby-memcached-0-1-0-released</id>
   <content type="html">&lt;p&gt;I just released &lt;a href="https://github.com/aurorafeint/jruby-memcached"&gt;jruby-memcached&lt;/a&gt; 0.1.0 gem, which is the fastest
jruby memcached client so far and it is also compatible with
memcached.gem.  The following is the story why I created
jruby-memcached gem.&lt;/p&gt;

&lt;p&gt;We are trying to migrate our service from ree to jruby. It's a big
project for us, as our repository is written from early 2009, it becomes
bigger and bigger, and nobody can promise migrating it to jruby without
any errors. Fortunately we are separating our service into different
pools, like one pool to handle high scores requests, one pool to handle
achievements requests, so our strategy is to migrate to jruby one pool
by one pool, it makes migrating processes easier, everytime we only focus
on one pool.&lt;/p&gt;

&lt;p&gt;This raises one problem, we are using &lt;a href="https://github.com/evan/memcached"&gt;evan's memcached gem&lt;/a&gt; which is
the fastest memcached client for MRI, but it isn't working in jruby, yes,
it's a ruby gem with c extention. But we have to solve the situation that
memcached client must work on both ree pool and jruby pool.&lt;/p&gt;

&lt;p&gt;The first idea in my mind is to use a jruby memcached client, after
googling I found &lt;a href="https://github.com/ikai/jruby-memcache-client"&gt;jruby-memcached-client&lt;/a&gt;, but soon I get to know
they can't be used together. jruby-memcached-client marshal dump the
value, encode the value to base64 then save to memcached, and memcached
gem only marshal dump, do not encode to base 64. Althrough I can fork
and change jruby-memcached-client, but there is still a string issue
passing from ruby to jave, I will mention it later.&lt;/p&gt;

&lt;p&gt;So I have to give up jruby-memcached-client, then I try to use pure ruby
memcached client like memcache-client or dalli. I pick up &lt;a href="https://github.com/mperham/dalli"&gt;dalli&lt;/a&gt;
which is faster than memcached-client, but after we deployed it on
production, we found memcached misses jump too high which we can't
afford, see&lt;/p&gt;

&lt;p&gt;&lt;img src="http://farm8.staticflickr.com/7128/7635380018_69f4cc5247.jpg" alt="Memcached misses" /&gt;&lt;/p&gt;

&lt;p&gt;we have to revert the release.&lt;/p&gt;

&lt;p&gt;I did some research about memcached, then I realized memcached and dalli
are incompatible, why? You may think memcached is simple, just set
key/value pair and get value based on key. It is right in common, but if
you have more than 1 memcached servers, memcached client should know
what key is on which memcached server, client must not fetch the key from
server1 now but fetch it from server2 10 minutes later.&lt;/p&gt;

&lt;p&gt;Let me introduce you 2 important client configurations&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hashing, it is the algorithm to convert you string key to long hash.&lt;/li&gt;
&lt;li&gt;Consistent Hashing, aka distribution in memcached gem, it is the
algorithm to map you generated hash to one of your memcached servers,
it promises low ratio cache reassigns when you add or remove a
memcached server. See more about consistent hashing on &lt;a href="http://en.wikipedia.org/wiki/Consistent_hashing"&gt;wikipedia&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;memcached uses fnv1_32 as hash algorithm by default and dalli uses
crc32, and their distribution algorithm are not compatible as well.&lt;/p&gt;

&lt;p&gt;Finally I decided to write a jruby memcached based on a java memcached
library by myself. After googling, I find 2 options: &lt;a href="http://code.google.com/p/xmemcached/"&gt;xmemcached&lt;/a&gt; and
&lt;a href="http://code.google.com/p/spymemcached/"&gt;spymemcached&lt;/a&gt;. The author of xmemcached shows the benchmark which
said xmemcached is a bit faster than spymemcached and it provides
libmemcached compatible hasing algorithm (although I was cheated in the
end :-) ), so I gave it a try first.&lt;/p&gt;

&lt;p&gt;xmemcached's LibmemcachedMemcachedSessionLocator is not compatbile with
libmemcached, at least not compatible with libmemcached 0.32 which is
used by memcached gem. I have to dive into libmemcached 0.32 source code
and override xmemcached MemcachedSessionLocator, and write a jruby gem
to wrap the xmemcached. (writing a jruby gem is not difficult, I
probably write a new post to introduce in the future) Then I released it
on our reverse pool, sending high traffic to see the performance, I'm
disappointed, memcached get time increased from 30+ ms to 60 ms, and it
generated about 200 threads for xmemcached (we have 30 memcached servers
and 2 memcached client instances).&lt;/p&gt;

&lt;p&gt;Quickly I replaced xmemcached to spymemcached, and memcached get time
decreased to 40+ ms and it only generates 2 threads, awesome. And its
hash and distribution algorithms are 100% compatible to libmemcached
0.32. You can read the source code in &lt;a href="https://github.com/aurorafeint/jruby-memcached/tree/master/src/main/java"&gt;src/main/java&lt;/a&gt; to see all
hacks I did for spymemcached.&lt;/p&gt;

&lt;p&gt;I mention a string issue above, it is when we passing a zlib deflated
value, like "x\234c?P?&lt;em&gt;?/?I\001\000\b8\002a", it changes to
"x?c?P?&lt;/em&gt;?/?8a" in java, so we can't pass deflated string directly,
instead we pass bytes.&lt;/p&gt;

&lt;p&gt;I also did some benchmark between memcached, jruby-memcached and dalli.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;in ruby-1.9.3
                           user     system      total        real
memcached &lt;span class="nb"&gt;set          &lt;/span&gt;1.110000   1.020000   2.130000 &lt;span class="o"&gt;(&lt;/span&gt;  4.592509&lt;span class="o"&gt;)&lt;/span&gt;
memcached get          0.970000   1.000000   1.970000 &lt;span class="o"&gt;(&lt;/span&gt;  4.172170&lt;span class="o"&gt;)&lt;/span&gt;
                           user     system      total        real
dalli &lt;span class="nb"&gt;set              &lt;/span&gt;8.330000   1.560000   9.890000 &lt;span class="o"&gt;(&lt;/span&gt; 10.094499&lt;span class="o"&gt;)&lt;/span&gt;
dalli get              8.530000   1.680000  10.210000 &lt;span class="o"&gt;(&lt;/span&gt; 10.331083&lt;span class="o"&gt;)&lt;/span&gt;

in jruby-1.6.7.2
                          user     system      total        real
jruby-memcached &lt;span class="nb"&gt;set   &lt;/span&gt;6.902000   0.000000   6.902000 &lt;span class="o"&gt;(&lt;/span&gt;  6.902000&lt;span class="o"&gt;)&lt;/span&gt;
jruby-memcached get   6.845000   0.000000   6.845000 &lt;span class="o"&gt;(&lt;/span&gt;  6.845000&lt;span class="o"&gt;)&lt;/span&gt;
                          user     system      total        real
dalli &lt;span class="nb"&gt;set            &lt;/span&gt;13.251000   0.000000  13.251000 &lt;span class="o"&gt;(&lt;/span&gt; 13.251000&lt;span class="o"&gt;)&lt;/span&gt;
dalli get            13.536000   0.000000  13.536000 &lt;span class="o"&gt;(&lt;/span&gt; 13.536000&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;see more &lt;a href="https://github.com/aurorafeint/jruby-memcached/blob/master/benchmark.rb"&gt;here&lt;/a&gt;, as you seen, both memcached and jruby-memcached are
2x faster than dalli.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/PEhRpgM-bIY" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>my railsconf 2012 video</title>
   <link href="http://www.huangzhimin.com/2012/06/14/my-railsconf-2012-video/" />
   <updated>2012-06-14T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/06/14/my-railsconf-2012-video</id>
   <content type="html">&lt;p&gt;This is my railsconf 2012 video on youtube.&lt;/p&gt;

&lt;iframe width="560" height="315"
src="http://www.youtube.com/embed/900BvuBzINI" frameborder="0"
allowfullscreen&gt;&lt;/iframe&gt;

&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/2PrXS9Iiry4" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>newrelic-workling released</title>
   <link href="http://www.huangzhimin.com/2012/06/07/newrelic-workling-released/" />
   <updated>2012-06-07T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/06/07/newrelic-workling-released</id>
   <content type="html">&lt;p&gt;We are using &lt;a href="https://github.com/purzelrakete/workling"&gt;workling&lt;/a&gt; with &lt;a href="http://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt; as our background
service and monitoring RabbitMQ on &lt;a href="https://scoutapp.com/"&gt;scout&lt;/a&gt;. Last month, we released
a new background job which generates tons of messages in RabbitMQ, then
messages in RabbitMQ queue kept growing, that means our workling
processes are not many enough to handle that messages. We fixed it by
reverting that job, using cron job to handle instead.&lt;/p&gt;

&lt;p&gt;We thought about this accident, and we decided to add &lt;a href="http://newrelic.com/"&gt;newrelic&lt;/a&gt;
support to measure workling instrument, so that we can have an idea
about how many messages generates for each job and how much does it cost
to consume one message.&lt;/p&gt;

&lt;p&gt;We finally released the &lt;a href="https://github.com/aurorafeint/newrelic-workling"&gt;newrelic-workling&lt;/a&gt; 1.0 gem today, thank
newrelic's help, we are the official support for newrelic workling, feel
free to ping us if you have any question. The following is the
screenshot for the workling instrument on newrelic.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://farm8.staticflickr.com/7094/7185962145_55cf2790f0.jpg" alt="workling
instrument" /&gt;&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/kqESH4kCHm4" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>bullet 4.0.0 released</title>
   <link href="http://www.huangzhimin.com/2012/05/09/bullet-4-released/" />
   <updated>2012-05-09T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/05/09/bullet-4-released</id>
   <content type="html">&lt;p&gt;&lt;a href="https://github.com/flyerhzm/bullet"&gt;bullet&lt;/a&gt; is designed to help you reduce the number of db queries, such as
adding eager loading to kill n+1 queries and removing unused eager
loadings.&lt;/p&gt;

&lt;p&gt;bullet works well in activerecord from 2.1 to 3.2 before, today I
released bullet 4.0.0, it starts to support mongoid (&gt;= 2.4.1) now.&lt;/p&gt;

&lt;p&gt;Why does bullet need to support mongoid?
Does mongo also have n+1 queries issue?&lt;/p&gt;

&lt;p&gt;The answer is yes, check out the &lt;a href="http://mongoid.org/performance.html"&gt;performance metric of mongoid eager
loading&lt;/a&gt;, about 40% performance improved. 1 year ago I already
created a gem &lt;a href="https://github.com/flyerhzm/mongoid-eager-loading"&gt;mongoid-eager-loading&lt;/a&gt; to add eager loading feature
in mongoid, it is deprecated as mongoid has already supported eager
loading natively.&lt;/p&gt;

&lt;p&gt;Be aware that bullet for mongoid doesn't support 2 level deep eager
loading and counter cache because they are not supported in mongoid so
far.&lt;/p&gt;

&lt;p&gt;What about mongomapper, I'd like to support it in future, but I have no
experience in it, does anybody have interests to implement it? Feel free
to contact me.&lt;/p&gt;

&lt;p&gt;Another big improvement in 4.0.0 is much better integration tests. If
you check out the source code, you will see I separate different
integration tests for activerecord 2, activerecord 3 and mongoid, I also
add these integration tests to different Gemfiles, and ask travis to
test all of them for bullet, see the &lt;a href="http://travis-ci.org/#!/flyerhzm/bullet/builds/1283580"&gt;build result&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any problems to use bullet gem, feel free to mail me, tweet
me or open an issue on github.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/n6Sw0fHa9b8" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>my presentation on railsconf 2012</title>
   <link href="http://www.huangzhimin.com/2012/05/03/my-presentation-on-railsconf-2012/" />
   <updated>2012-05-03T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/05/03/my-presentation-on-railsconf-2012</id>
   <content type="html">&lt;p&gt;I attended and spoke at railsconf 2012 last week, the following is my
presentation&lt;/p&gt;

&lt;div style="width:425px" id="__ss_12677703"&gt; &lt;strong
style="display:block;margin:12px 0 4px"&gt;&lt;a
href="http://www.slideshare.net/flyerhzm/semi-automatic-code-review"
title="Semi Automatic Code Review" target="_blank"&gt;Semi Automatic Code
Review&lt;/a&gt;&lt;/strong&gt; &lt;iframe
src="http://www.slideshare.net/slideshow/embed_code/12677703"
width="425" height="355" frameborder="0" marginwidth="0"
marginheight="0" scrolling="no"&gt;&lt;/iframe&gt; &lt;div style="padding:5px 0
12px"&gt; View more &lt;a href="http://www.slideshare.net/"
target="_blank"&gt;presentations&lt;/a&gt; from &lt;a
href="http://www.slideshare.net/flyerhzm" target="_blank"&gt;Richard
Huang&lt;/a&gt; &lt;/div&gt; &lt;/div&gt;


&lt;p&gt;If you have any questions or suggestions, feel free to email me, tweet
me or open issues on github.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/q1sTTbfb17k" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>redis mget/mset vs get/set</title>
   <link href="http://www.huangzhimin.com/2012/04/05/redis-mget-mset-vs-get-set/" />
   <updated>2012-04-05T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/04/05/redis-mget-mset-vs-get-set</id>
   <content type="html">&lt;p&gt;Our application uses redis a lot to perform large numbers of data
reads/writes. But we didn't use it well enough, e.g. we call redis get
and set in loop, just like touching mysql and memcache many times, it
takes a long time to send multiple redis commands, if we can reduce the
commands, it saves on round trip time.&lt;/p&gt;

&lt;p&gt;The following script is used to bencharmark different commands count.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;redis&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;benchmark&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
&lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis set&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;key&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis get&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;key&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis mset with 1000&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;yek&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;
      &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis mget with 1000&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;yek&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis mset with 100&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;eky&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;
      &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis mget with 100&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;eky&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis mset with 10&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;eyk&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;
      &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis mget with 10&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;eyk&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis mset with 1&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;kye&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;redis mget with 1&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;kye&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is the benchmark result.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#                             user   system      total        real&lt;/span&gt;
&lt;span class="c1"&gt;#  redis set              0.280000   0.170000   0.450000 (  0.809112)&lt;/span&gt;
&lt;span class="c1"&gt;#  redis get              0.290000   0.160000   0.450000 (  0.806711)&lt;/span&gt;
&lt;span class="c1"&gt;#  redis mset with 1000   0.070000   0.020000   0.090000 (  0.148474)&lt;/span&gt;
&lt;span class="c1"&gt;#  redis mget with 1000   0.080000   0.020000   0.100000 (  0.142837)&lt;/span&gt;
&lt;span class="c1"&gt;#  redis mset with 100    0.050000   0.000000   0.050000 (  0.067859)&lt;/span&gt;
&lt;span class="c1"&gt;#  redis mget with 100    0.050000   0.010000   0.060000 (  0.063040)&lt;/span&gt;
&lt;span class="c1"&gt;#  redis mset with 10     0.040000   0.000000   0.040000 (  0.060200)&lt;/span&gt;
&lt;span class="c1"&gt;#  redis mget with 10     0.050000   0.000000   0.050000 (  0.057818)&lt;/span&gt;
&lt;span class="c1"&gt;#  redis mset with 1      0.040000   0.000000   0.040000 (  0.062318)&lt;/span&gt;
&lt;span class="c1"&gt;#  redis mget with 1      0.050000   0.000000   0.050000 (  0.057483)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It's obvious that less redis commands means fast running time.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/-flesWzll60" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>master slave replication in rails</title>
   <link href="http://www.huangzhimin.com/2012/03/28/master-slave-replication-in-rails/" />
   <updated>2012-03-28T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/03/28/master-slave-replication-in-rails</id>
   <content type="html">&lt;h3&gt;Introduction&lt;/h3&gt;

&lt;p&gt;By default activerecord works well with single db, it's applicable for
most of websites with small/medium traffic, but if you website grows
fast and gets much more reads than writes, you should definitly set up
master slave replication for your databse. All inserts/updates are sent
to master db, and reads are sent to slave db, it will reduce read load
on your master db.&lt;/p&gt;

&lt;p&gt;Master slave replication allows to set up as many slave dbs as you need,
it's scalable, that means you can easily increase you db read throughput
by adding more slave dbs. It also allows you to move some tasks like
analytics on slave db without affecting your master db.&lt;/p&gt;

&lt;h3&gt;Replication in rails&lt;/h3&gt;

&lt;p&gt;How do we config master slave replication in rails app? There are a lot
of &lt;a href="https://www.ruby-toolbox.com/categories/Active_Record_Sharding"&gt;choices&lt;/a&gt;, pick up one and setup according to its document. I
don't want to discuss about these tools here, I will tell you how to use
master slave replication in rails above these tools.&lt;/p&gt;

&lt;h3&gt;Problems&lt;/h3&gt;

&lt;p&gt;Master slave replication looks well, but it has a big problem in
practice - replication lag. There is a lag between data inserted in
master db and sync to slave db, let's see a case.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;a user create a post on your application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the post is inserted to master db.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;your application redirects user to post show page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;your application read from slave db, but the post is not sync yet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a 404 page is shown. :-(&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the post is sync to slave db. (too late)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Lots of similar issues will raise after you applying master slave
replication, how to solve them?&lt;/p&gt;

&lt;h3&gt;Solution&lt;/h3&gt;

&lt;p&gt;The solution is send some reads to master db to promise get fresh data.&lt;/p&gt;

&lt;p&gt;By default all reads will be sent to master db in one db transaction,
like&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="sql"&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In the following cases I will send reads to master db as well&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;queries in background job, like delayed_job, resque, workling, etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;clas&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;ActiveRecord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;
  &lt;span class="n"&gt;after_create&lt;/span&gt; &lt;span class="ss"&gt;:notify&lt;/span&gt;

  &lt;span class="kp"&gt;protected&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;
    &lt;span class="ss"&gt;Delayed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;DelayedJob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:NotifyAdmin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DelayedJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotifyAdmin&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It's probably the post does not exist when reading it from slave db in
background job.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;queries in the request which follows a redirect reponse&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;
      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;post_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This case is too common, creating/updating then redirecting, if the
resource is not sync to slave db before next request, user will get a
404 page or get some fake data.&lt;/p&gt;

&lt;p&gt;We know when we should explictly send reads to master db, but how can we
do that. It's&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="ss"&gt;ActiveRecord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_master&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Almost all of replication gem provide with_master method, any queries in
the block will be sent to master db. I added a monkey patch to background
job, wrapping it with with_master.&lt;/p&gt;

&lt;p&gt;I added add a monkey patch to action controller as well, adding a parameter
if the response is a redirect, then add a around_filter to controller to
check if the reads in such request should be sent to master or slave db.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;ActionController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;
  &lt;span class="n"&gt;around_filter&lt;/span&gt; &lt;span class="ss"&gt;:manage_slaving&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;manage_slaving&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;force_master?&lt;/span&gt;
      &lt;span class="ss"&gt;ActiveRecord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_master&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;force_master? is a convenient way to manage your master/slave db on
controller levels, you can also enable/disalbe master/slave for some
specfied requests.&lt;/p&gt;

&lt;p&gt;Finally test your application and add ActiveRecord::Base.with_mater {}
if necessary.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/TFiLznlJX0c" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>bullet 2.3.0 released</title>
   <link href="http://www.huangzhimin.com/2012/03/25/bullet-2.3.0-released/" />
   <updated>2012-03-25T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/03/25/bullet-2.3.0-released</id>
   <content type="html">&lt;p&gt;&lt;a href="https://github.com/flyerhzm/bullet"&gt;bullet&lt;/a&gt; is a gem to help you increase your application's performance
by reducing the number of sql requests it makes. Today I released bullet
2.3.0 to better support rails 3.1 and 3.2 and performance improved. It's
a long time I didn't do any changes to bullet, let me tell you the story
I work for bullet 2.3.0.&lt;/p&gt;

&lt;p&gt;At the beginning of this month, bullet got its 1000th watcher on github,
I realized it's time to improve it e.g. speed up and compatible with
edge rails.&lt;/p&gt;

&lt;p&gt;The first thing I did is to refactor tests. Before I created several
rspec tests, but they are more like integration tests instead of unit
tests, so I move them to spec/integration/ directory. Then I added a
bunch of test units to cover all codes, which can promise the
correctness of further code refactors. I also use &lt;a href="https://github.com/guard/guard"&gt;guard&lt;/a&gt; instead of
watchr to do auto tests, why I preferred guard? It's much easier and has
more extensions, like guard-rspec.&lt;/p&gt;

&lt;p&gt;Then I moved AR models, which are used for integration tests, from
integration tests to spec/models, and I also moved db connection, db
schema and db seed to spec/support/, moved test helpers to spec/support/
as well. Now my tests looks much cleaner and run much faster (only
connect db once).&lt;/p&gt;

&lt;p&gt;After refactoring tests, I tried to improve the bullet performance, I
already created a &lt;a href="https://github.com/flyerhzm/bullet/blob/master/perf/benchmark.rb"&gt;benchmark script&lt;/a&gt; before, bullet 2.2.1 with rails
3.0.12 spent 30s to complete&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="text"&gt;bullet 2.2.1 with rails 3.0.12
                                                                             user     system      total        real
Querying &amp;amp; Iterating 1000 Posts with 10000 Comments and 100 Users       29.970000   0.270000  30.240000 ( 30.452083)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then I used perftools.rb to measure cpu time for methods, the result is
garbage_collector, String#=~ and Kernel#caller&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;garbage_collector, it depends on how many objects allocated&lt;/li&gt;
&lt;li&gt;String#=~, bullet use regexp to check if caller contains load_target&lt;/li&gt;
&lt;li&gt;Kernel#caller, bullet uses caller to tell what codes caused n+1 query&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;I found the easiest is to mitigate String#=~, as bullet only check
regexp with constant string load_target, so I simply used
.include?("load_target") instead.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="text"&gt;bullet 2.3.0 with rails 3.0.12
                                                                             user     system      total        real
Querying &amp;amp; Iterating 1000 Posts with 10000 Comments and 100 Users       26.120000   0.430000  26.550000 ( 27.179304)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;another change is to store object's ar_key instead of object itself.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="text"&gt;{&amp;lt;#Post id:1, title:&amp;quot;post1&amp;quot;, body:&amp;quot;post body&amp;quot;, created_at:..., updated_at:...&amp;gt; =&amp;gt; [:comments]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;to&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="text"&gt;{&amp;quot;Post:1&amp;quot; =&amp;gt; [:comments]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;it speeds up hash comparison time and save the hash size.&lt;/p&gt;

&lt;p&gt;I also hacked ActiveRecord::Associations::SingularAssociation#reader
instead of ActiveRecord::Associations::Association#load_target for rails
3.1 and 3.2, it fixes activerecord 3.1 and 3.2 compatibility, there is
no need to call caller in Association#load_target, it runs much faster
in rails 3.1 and 3.2, the following is the benchmark result&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="text"&gt;bullet 2.3.0 with rails 3.2.2
                                                                             user     system      total        real
Querying &amp;amp; Iterating 1000 Posts with 10000 Comments and 100 Users       16.460000   0.190000  16.650000 ( 16.968246)

bullet 2.3.0 with rails 3.1.4
                                                                             user     system      total        real
Querying &amp;amp; Iterating 1000 Posts with 10000 Comments and 100 Users       14.600000   0.130000  14.730000 ( 14.937590)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Enjoy the new &lt;a href="https://github.com/flyerhzm/bullet"&gt;bullet&lt;/a&gt; gem!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/GoSBid9uxHM" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>multiple_mailers - send emails by different smtp accounts</title>
   <link href="http://www.huangzhimin.com/2012/03/21/multiple_mailers-send-emails-by-multiple-smtp-accounts/" />
   <updated>2012-03-21T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/03/21/multiple_mailers-send-emails-by-multiple-smtp-accounts</id>
   <content type="html">&lt;p&gt;I use gmail to send email notifications on my website, it's really easy
to build based on actionmailer&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="ss"&gt;ActionMailer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;smtp_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;:address&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;smtp.gmail.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:port&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;587&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:domain&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;railsbp.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:authentication&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:plain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:user_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;notification@railsbp.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:password&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;But I found it does not allow to setup 2 different smtp accounts, e.g. I
want to send notification email with notification@railsbp.com and send
exception notifier email with exception.notifier@railsbp.com, after
googling, I hacked my mailer classes with&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationMailer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;ActionMailer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;production?&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;smtp_settings&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;YAML&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/mailers.yml&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;exception_notifier&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="vc"&gt;@@smtp_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="ss"&gt;:address&lt;/span&gt;              &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;address&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:port&lt;/span&gt;                 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;port&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:domain&lt;/span&gt;               &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;domain&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:authentication&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;authentication&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:user_name&lt;/span&gt;            &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;user_name&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;:password&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;then add a new config file config/mailers.yml&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="yaml"&gt;&lt;span class="l-Scalar-Plain"&gt;production&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;
  &lt;span class="l-Scalar-Plain"&gt;common&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;common&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;address&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;smtp.gmail.com&amp;#39;&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;port&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;587&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;domain&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;rails-bestpractices.com&amp;#39;&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;authentication&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;plain&amp;#39;&lt;/span&gt;

  &lt;span class="l-Scalar-Plain"&gt;notification&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*common&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;user_name&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;notification@rails-bestpractices.com&amp;#39;&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;password&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;

  &lt;span class="l-Scalar-Plain"&gt;exception.notifier&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*common&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;user_name&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;exception.notifier@rails-bestpractices.com&amp;#39;&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;password&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;that allows me to setup one smtp account per actionmailer class, keep in
mind that you should only hack smtp_settings for what environment you
really want to send emails (here is production), if you don't check
Rails.env, it will send email even in development and test environments.&lt;/p&gt;

&lt;p&gt;Now it works fine, I can send emails by as many smtp accounts as I like, but
it looks ugly, I don't like hacking codes all over my mailer classes. So I
abstract it to a new gem &lt;a href="https://github.com/flyerhzm/multiple_mailers"&gt;multiple_mailers&lt;/a&gt;, like the hack above, you
should define config file config/mailers.yml and for each mail class,
what you only need is to declare its mailer account name&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationMailer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;ActionMailer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;
  &lt;span class="n"&gt;mailer_account&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;notification&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExceptionNotifier&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Notifier&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;ActionMailer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;
    &lt;span class="n"&gt;mailer_account&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;exception.notifier&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/ccIva4tC_Xk" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>passenger with http_gzip_static_module</title>
   <link href="http://www.huangzhimin.com/2012/02/27/passenger-with-http-gzip-static-module/" />
   <updated>2012-02-27T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2012/02/27/passenger-with-http-gzip-static-module</id>
   <content type="html">&lt;p&gt;Rails 3.1 has been released for a long time, asset pipeline becomes more
and more popular, I also upgraded my rails website.&lt;/p&gt;

&lt;p&gt;I used nginx + passenger for my rails projects, but nginx only supports
dynamic gzip support (compress in runtime), there is a
http_gzip_static_module for nginx, which can make full use of rails
asset pipeline.&lt;/p&gt;

&lt;p&gt;I don't like the way to customize my Nginx installation during passenger
installation, I found there is a &lt;a href="https://github.com/FooBarWidget/passenger/pull/35"&gt;pull request&lt;/a&gt; to add
http_gzip_static_module, so I changed to source code of passenger gem,
then installed nginx as default. :-)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/XMvZs8uZmKk" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>rake arguments</title>
   <link href="http://www.huangzhimin.com/2011/12/13/rake-arguments/" />
   <updated>2011-12-13T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/12/13/rake-arguments</id>
   <content type="html">&lt;p&gt;Long ago I began to write some rake tasks, it's simple but doesn't have
an instruction about how to add arguments to a rake task. What I did
before is to use ruby environment variables.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:try_argument&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;GLOBAL_ARGUMENT1&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;GLOBAL_ARGUMENT2&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;GLOBAL_ARGUMENT1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;xxx&lt;/span&gt; &lt;span class="no"&gt;GLOBAL_ARGUMENT2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;yyy&lt;/span&gt; &lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;try_argument&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As you seen, I have to set the global environment variable to pass the
arguement to a rake task.&lt;/p&gt;

&lt;p&gt;But there is another way to pass the arguments to rake task via []&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:try_argument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:key2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_defaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:key1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:key2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;try_argument&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;xxx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yyy&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and if there is dependent task, you should define it like&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:try_argument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:key2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:environment&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_defaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:key1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:key2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;try_argument&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;xxx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yyy&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It looks like the difference between hash arguments and normal arguments.&lt;/p&gt;

&lt;p&gt;Both of them have disadvantage:&lt;/p&gt;

&lt;p&gt;ENV arguments also changes the system env variables
normal arguments do not make sense when calling, difficult to remember
the meanings of arguments.&lt;/p&gt;

&lt;p&gt;Both work fine, it depends on you to use which one.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/LANhYCe6IyU" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>passenger with redis</title>
   <link href="http://www.huangzhimin.com/2011/12/12/passenger-with-redis/" />
   <updated>2011-12-12T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/12/12/passenger-with-redis</id>
   <content type="html">&lt;p&gt;Today I encountered an issue that passenger forks too many workers
than what we set (6) on qa servers. I used strace, the passenger worker
is blocked by failed to writing to a socket, like&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;15, &lt;span class="o"&gt;[]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;13&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;58, 915000&lt;span class="o"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;fd 13 is a socket.&lt;/p&gt;

&lt;p&gt;I also tried netstat and found the status for some redis socket
connections are CLOSE_WAIT.&lt;/p&gt;

&lt;p&gt;So I judged this is the problem the ruby redis clients are not closed
correctly. This reminds me that passenger fork() nature, I checked our
source codes, unfortunately, we didn't do anything special for passenger
fork.&lt;/p&gt;

&lt;p&gt;This is the &lt;a href="https://github.com/ezmobius/redis-rb/wiki/redis-rb-on-Phusion-Passenger"&gt;link&lt;/a&gt; tells you how to close the redis connection after
passenger forks a worker. After deploy the new codes to qa servers,
passenger never forks more workers than we expected. But the workers
still hang up according strace result, that means some workers keep
inactive status, they won't be able to handle any requests. Wooops...&lt;/p&gt;

&lt;p&gt;I looked through the redis-rb source codes, we used redis 2.0.5, it
didn't handle TIMEOUT error and always retry writing to redis.
Fortunately, the latest redis version is 2.2.2 and it already fixed this
issue, retry 3 times, if still failed, the release the connection.&lt;/p&gt;

&lt;p&gt;Now it works fine, no unexpected additional passenger workers and no
unexpected inactive workers.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/sLBv2gaALio" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>avoid committing git conflicts</title>
   <link href="http://www.huangzhimin.com/2011/11/14/avoid-committing-git-conflicts/" />
   <updated>2011-11-14T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/11/14/avoid-committing-git-conflicts</id>
   <content type="html">&lt;p&gt;I made a mistake when merging branch last week, I forgot to remove a
conflict syntax "&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD" and push it to remote repository. It
breaks other one's development. So stupid to make such mistake.&lt;/p&gt;

&lt;p&gt;To avoid making such mistake anymore, I write a git hook
.git/hooks/pre-commit to check conflict syntax "&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;" and "&gt;&gt;&gt;&gt;&gt;&gt;"&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#!/usr/bin/env ruby&lt;/span&gt;

&lt;span class="sb"&gt;`git diff-index --name-status HEAD`&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;status_with_filename&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;status_with_filename&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\s+/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;D&amp;#39;&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gets&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;ERROR: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is conflict&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It will prevent you from committing conflicts.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/EIs594C3Now" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>after_commit</title>
   <link href="http://www.huangzhimin.com/2011/11/06/after_commit/" />
   <updated>2011-11-06T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/11/06/after_commit</id>
   <content type="html">&lt;p&gt;We are using RabbitMQ as our message queue system, ruby client is
workling. This week we encountered a strange issue, we create a
notification, and define an after_create callback to ask workling to
find that notification and then push the notification to twitter or
facebook, it works fine except that sometimes it will raise an error
said "can't find the notification with the specified ID"&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;ActiveRecord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;
  &lt;span class="n"&gt;after_create&lt;/span&gt; &lt;span class="ss"&gt;:asyns_send_notification&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;async_send_notification&lt;/span&gt;
    &lt;span class="no"&gt;NotificationWorker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async_send_notification&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:notification_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationWorker&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;Workling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:notification_id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It's wierd the notification_id is passed to NotificationWorker, that
means the notification is already created, the notification is supposed
to be existed.&lt;/p&gt;

&lt;p&gt;After talking with MySQL DBA, we find the problem is the find sql is
executed before insert transaction is committed.&lt;/p&gt;

&lt;p&gt;Let me describe it step by step.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Notification sends "Transaction Begin" command&lt;/li&gt;
&lt;li&gt;Notification sends "INSERT" command&lt;/li&gt;
&lt;li&gt;Notification gets "next sequence value" as new object id&lt;/li&gt;
&lt;li&gt;Notification sends "new object id" to NotificationWorker&lt;/li&gt;
&lt;li&gt;NotificationWorker sends "SELECT" command to find notification object&lt;/li&gt;
&lt;li&gt;Notification sends "Transaction Commit" command&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;As you seen, at step 5, the new notification is not existed in the mysql
database yet, so the error "Not found" will be raised.&lt;/p&gt;

&lt;p&gt;To solve this issue, we can use after_commit callback.&lt;/p&gt;

&lt;p&gt;In rails 2.x, we should install after_commit gem, in rails 3.x,
after_commit callback is supported by default.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;ActiveRecord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;
  &lt;span class="n"&gt;after_commit_on_create&lt;/span&gt; &lt;span class="ss"&gt;:asyns_send_notification&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;async_send_notification&lt;/span&gt;
    &lt;span class="no"&gt;NotificationWorker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async_send_notification&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:notification_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So Notification asks NotificationWorker to run only after the
transaction is committed.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/DWARuFDcONc" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>reset_counters in rails</title>
   <link href="http://www.huangzhimin.com/2011/10/31/reset_counter-in-rails/" />
   <updated>2011-10-31T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/10/31/reset_counter-in-rails</id>
   <content type="html">&lt;p&gt;I thought reset_counters method is to reset a counter_cache column to be
0, but it is not. After trying several times, I finally realize that
reset_counters is to update the value of counter_cache column to the
exact count of associations. The usecase of reset_counters is when you
add the counter_cache in migration and update the counter_cache value,
like&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;up&lt;/span&gt;
  &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:comments_count&lt;/span&gt;
  &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset_counters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;it will add comments_count column to posts table, and calculate the
comments count for each post, and set it to posts' comments_count
column.&lt;/p&gt;

&lt;p&gt;I didn't find a method to reset the counter_cache column to be 0, why?
Because counter_cache is used to cache the association count, it will be
incremented and decremeneted automatically, you should never reset it 0.
If you find you need to reset counter_cache to 0, that means it's a
wrong usage of counter_cache.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/K883SdPUDIk" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>use rspec filter to speed up tests</title>
   <link href="http://www.huangzhimin.com/2011/10/21/rspec-filter/" />
   <updated>2011-10-21T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/10/21/rspec-filter</id>
   <content type="html">&lt;p&gt;Rspec 2 introduce a very efficient way to test only one test or one test
suit, it's filter_run.&lt;/p&gt;

&lt;p&gt;You should first add filter_run in rspec/spec_helper.rb&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter_run&lt;/span&gt; &lt;span class="ss"&gt;:focus&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then you can tell rspec to test only one test you are focused by&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;should focus now&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:focus&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;rspec will only test this spec, :focus =&gt; true can be applied on
describe/context as well.&lt;/p&gt;

&lt;p&gt;One problem is that if there is no :focus =&gt; true on your tests, rspec
will do nothing, but most of time we are expecting to test all specs if
no focus is true, so you should add a line to spec_helper as well.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run_all_when_everything_filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As the name implies, rspec will test all specs if no focus filter.&lt;/p&gt;

&lt;p&gt;Another you may interest that you can also define filter_run_excluding&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter_run_excluding&lt;/span&gt; &lt;span class="ss"&gt;:slow&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;rspec will run all specs except what specs are marked as slow.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/e852CiSntuM" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>rubykaigi presentation</title>
   <link href="http://www.huangzhimin.com/2011/07/17/rubykaigi-presentation/" />
   <updated>2011-07-17T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/07/17/rubykaigi-presentation</id>
   <content type="html">&lt;p&gt;My presentation in RubyKaigin 2011 today.&lt;/p&gt;

&lt;div style="width:425px" id="__ss_8617337"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/flyerhzm/rails-best-practicesrubykaigi" title="Rails best practices_rubykaigi"&gt;Rails best practices_rubykaigi&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse8617337" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=railsbestpracticesrubykaigi-110717075124-phpapp02&amp;stripped_title=rails-best-practicesrubykaigi&amp;userName=flyerhzm" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse8617337" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=railsbestpracticesrubykaigi-110717075124-phpapp02&amp;stripped_title=rails-best-practicesrubykaigi&amp;userName=flyerhzm" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/flyerhzm"&gt;Richard Huang&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;


&lt;p&gt;and the video is here: &lt;a href="http://www.ustream.tv/recorded/16051491"&gt;http://www.ustream.tv/recorded/16051491&lt;/a&gt;&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/RsiOA3vX5mE" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>beijing ruby线下活动</title>
   <link href="http://www.huangzhimin.com/2011/03/27/beijing-ruby-event/" />
   <updated>2011-03-27T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/03/27/beijing-ruby-event</id>
   <content type="html">&lt;p&gt;周日在北京的ruby线下活动的ppt&lt;/p&gt;

&lt;div style="width:425px" id="__ss_7409659"&gt; &lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/flyerhzm/rails-best-practices-7409659" title="使用Rails best practices做代码审查"&gt;使用Rails best practices做代码审查&lt;/a&gt;&lt;/strong&gt; &lt;object id="__sse7409659" width="425" height="355"&gt; &lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=railsbestpractices-110327215428-phpapp02&amp;stripped_title=rails-best-practices-7409659&amp;userName=flyerhzm" /&gt; &lt;param name="allowFullScreen" value="true"/&gt; &lt;param name="allowScriptAccess" value="always"/&gt; &lt;embed name="__sse7409659" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=railsbestpractices-110327215428-phpapp02&amp;stripped_title=rails-best-practices-7409659&amp;userName=flyerhzm" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt; &lt;/object&gt; &lt;div style="padding:5px 0 12px"&gt; View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/flyerhzm"&gt;Richard Huang&lt;/a&gt; &lt;/div&gt; &lt;/div&gt;

&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/WlK3h6VrMdc" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Upgrade Mongoid - Multiple databases</title>
   <link href="http://www.huangzhimin.com/2011/03/22/upgrade-mongoid-multiple-database/" />
   <updated>2011-03-22T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/03/22/upgrade-mongoid-multiple-database</id>
   <content type="html">&lt;p&gt;My recent post &lt;a href="http://www.huangzhimin.com/2011/01/14/use-different-mongodb-instances-in-mongoid/"&gt;Use different mongodb instances in mongoid&lt;/a&gt; tells you how to use multiple databases, it looks good, but mongoid began to support multiple databases itself from mongoid.2.0.0.rc.1, much better than my hack.&lt;/p&gt;

&lt;p&gt;It's really easy to use, first, you should define multiple databases in mongoid.yml like&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="yaml"&gt;&lt;span class="l-Scalar-Plain"&gt;development&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;
  &lt;span class="l-Scalar-Plain"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*defaults&lt;/span&gt;
  &lt;span class="l-Scalar-Plain"&gt;host&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;localhost&lt;/span&gt;
  &lt;span class="l-Scalar-Plain"&gt;database&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;main_mongo_instance&lt;/span&gt;
  &lt;span class="l-Scalar-Plain"&gt;databases&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;
    &lt;span class="l-Scalar-Plain"&gt;other_mongo_instance_name&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;
      &lt;span class="l-Scalar-Plain"&gt;database&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;other_mongo_instance&lt;/span&gt;
      &lt;span class="l-Scalar-Plain"&gt;host&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As you seen, besides the common database param, I have defined a new param databases, you should define the mongo instance name with database and host name, and of course, you can define as many mongo instances as you need.&lt;/p&gt;

&lt;p&gt;Then, you can choose which mongo instance to use in mongoid model.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="ss"&gt;Mongoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Document&lt;/span&gt;

  &lt;span class="n"&gt;set_database&lt;/span&gt; &lt;span class="ss"&gt;:other_mongo_instance_name&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;set_database method tells mongoid that the model will use another mongo instance instead of the main mongo instance, here we use the name other_mongo_instance_name which should exactly be the same with the name defined in mongoid.yml. If you don't say anything, it will use the main_mongo_instance.&lt;/p&gt;

&lt;p&gt;So all the users data will be stored to other_mongo_instance_name, and the other data will be stored to main_mongo_instance. Great!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/NbTQzbVe02o" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Upgrade Mongoid - update_attribute</title>
   <link href="http://www.huangzhimin.com/2011/03/21/upgrade-mongoid-update_attribute/" />
   <updated>2011-03-21T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/03/21/upgrade-mongoid-update_attribute</id>
   <content type="html">&lt;p&gt;Before mongoid 2.0.0.rc.6, there is no update_attribute method for Mongoid::Document, it makes me unhappy. As in ActiveRecord world, I always use update_attribute to change one attribute and use update_attributes to change two or more attributes.&lt;/p&gt;

&lt;p&gt;It's a good news that mongoid introduces the update_attribute method from 2.0.0.rc.6, now I can follow my practice in mongoid.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;New Post&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;New Post&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;New Body&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/KKzTDqTjRys" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Upgrade Mongoid - Many to many association</title>
   <link href="http://www.huangzhimin.com/2011/03/08/upgrade-mongoid-many-to-many-associations/" />
   <updated>2011-03-08T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/03/08/upgrade-mongoid-many-to-many-associations</id>
   <content type="html">&lt;p&gt;Before mongoid 2.0.0.rc1, there is no default support for many to many association. So we use join document (aka join table in relational database) to implement the many to many association.&lt;/p&gt;

&lt;p&gt;For example, we have two documents users and accounts, one user has many accounts and one account contains many users, to establish the many to many relationship between users and accounts, we create a new document named user_accounts, the document looks like&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;4d76d3a70bdb822d08000001&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3b80bdb822d080015b3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;account_id&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3b90bdb822d080015b7&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and the models are defined as follows&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="ss"&gt;Mongoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Document&lt;/span&gt;
  &lt;span class="n"&gt;references_many&lt;/span&gt; &lt;span class="ss"&gt;:user_accounts&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="ss"&gt;Mongoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Document&lt;/span&gt;
  &lt;span class="n"&gt;references_many&lt;/span&gt; &lt;span class="ss"&gt;:user_accounts&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserAccount&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="ss"&gt;Mongoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Document&lt;/span&gt;
  &lt;span class="n"&gt;referenced_in&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;
  &lt;span class="n"&gt;referenced_in&lt;/span&gt; &lt;span class="ss"&gt;:account&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Are you familiar with it, it's what activerecord did for many to many association.&lt;/p&gt;

&lt;p&gt;I'm glad that mongoid began to support many to many association after mongoid 2.0.0.rc1, the new syntax is "referenes_and_referenced_in_many".&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="ss"&gt;Mongoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Document&lt;/span&gt;
  &lt;span class="n"&gt;references_and_referenced_in_many&lt;/span&gt; &lt;span class="ss"&gt;:accounts&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="ss"&gt;Mongoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Document&lt;/span&gt;
  &lt;span class="n"&gt;references_and_referenced_in_many&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;We don't need the join document any more. The implementation of mongoid is different with activerecord, it uses array attribute to store the relationship at both sides. Like&lt;/p&gt;

&lt;p&gt;These are user documents&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;4d76d3a90bdb822d08000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account_ids&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3aa0bdb822d0800001b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3aa0bdb822d0800001d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3aa0bdb822d08000017&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;4d76d3a80bdb822d08000005&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account_ids&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3aa0bdb822d08000017&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3a90bdb822d08000015&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And these are account documents&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;4d76d3aa0bdb822d08000017&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user_ids&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3a80bdb822d08000005&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3a90bdb822d08000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3a90bdb822d0800000d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;4d76d3aa0bdb822d0800001b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user_ids&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3a90bdb822d08000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;4d76d3a90bdb822d08000011&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As mongodb support the Array type, it is really easy to maintain the many to many relationship.&lt;/p&gt;

&lt;p&gt;Btw, if you use&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;references_many&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:stored_as&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;before, you will receive a runtime error. You should use&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;references_and_referenced_in_many&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;instead.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/Rw1cdqFe1s8" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Upgrade Mongoid - Hash arguments for group</title>
   <link href="http://www.huangzhimin.com/2011/03/01/upgrade-mongoid-hash-arguments-for-group/" />
   <updated>2011-03-01T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/03/01/upgrade-mongoid-hash-arguments-for-group</id>
   <content type="html">&lt;p&gt;You will receive a warning for the group method call after upgrading mongoid.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;Collection#group no longer take a list of paramters. This usage is deprecated.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;exactly this is because mongo gem changes the group method definition.&lt;/p&gt;

&lt;p&gt;Before&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ad_id&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;conditions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ad_id&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;$in&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ad_ids&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;initial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;impressions&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;clicks&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;reduce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;a reduce javascript function&amp;quot;&lt;/span&gt;

&lt;span class="no"&gt;AdStat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;After&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ad_id&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;conditions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ad_id&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;$in&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ad_ids&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;initial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;impressions&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;clicks&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;reduce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;a reduce javascript function&amp;quot;&lt;/span&gt;

&lt;span class="no"&gt;AdStat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:conditions&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:initial&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:reduce&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is the usage of hash arguments, it makes the group calling more readable.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/4nSk_mWeD-U" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Upgrade Mongoid - Default Type for Field</title>
   <link href="http://www.huangzhimin.com/2011/01/28/upgrade-mongoid-default-type-for-field/" />
   <updated>2011-01-28T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/01/28/upgrade-mongoid-default-type-for-field</id>
   <content type="html">&lt;p&gt;If you have watched the episode about &lt;a href="http://railscasts.com/episodes/238-mongoid"&gt;mongoid&lt;/a&gt; from railscast, ryanb removed the default type String for field, like&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;can be written as&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;but it is not valid from mongoid.2.0.0.rc.1 again, the default type of field is changed from String to Object, that means we should explicitly set the type for each field.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/FJhDN4EW12M" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Upgrade Mongoid - Write Tests First</title>
   <link href="http://www.huangzhimin.com/2011/01/27/upgrade-mongoid-write-tests-first/" />
   <updated>2011-01-27T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/01/27/upgrade-mongoid-write-tests-first</id>
   <content type="html">&lt;p&gt;Mongoid is one of the popular Object Document Mappings between Ruby and Mongo, and it is still evolving. We began to use mongoid 2.0.0.beta.20 several weeks ago, the author of mongoid @durran said he wanted to release the 2.0.0 last week (As you know 2.0.0 is still not released yet, but he really did a lot of awesome work), so we tried the version 2.0.0.rc.6 to prepare upgrading to final 2.0.0.&lt;/p&gt;

&lt;p&gt;I'm working on upgrading mongoid from 2.0.0.beta.20 to 2.0.0.rc.6 these days. I'm willing to write several posts to share my experience about upgrading.&lt;/p&gt;

&lt;p&gt;At the first post, I just want you keep in mind that &lt;strong&gt;don't do any upgrading before you write tests for your models&lt;/strong&gt;. There are many api changes between mongoid 2.0.0.beta.20 and 2.0.0.rc.6, I can't imagine how to upgrade without tests, as our project has almost 30 models and 100 view pages, I can't check the models and views one by one. Luckily, we have built many rspec tests for models and cucumber tests.&lt;/p&gt;

&lt;p&gt;It's expected that many test failures raised after upgrading, if I fixed all the failures, the job to upgrade is complete.&lt;/p&gt;

&lt;p&gt;I have to say I like such upgrading job, I read the source codes of mongoid, checked git logs, sometimes thought why they made such changes, and always learned a lot from reading source codes. :-)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/fy_RZxDpzT4" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Migrate Custom Blog to Jekyll and Disqus</title>
   <link href="http://www.huangzhimin.com/2011/01/20/migrate-custom-blog-to-jekyll-and-disqus/" />
   <updated>2011-01-20T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/01/20/migrate-custom-blog-to-jekyll-and-disqus</id>
   <content type="html">&lt;p&gt;I wrote my blog system by myself about 3 years ago, using rails. It's good, but not cool enough, I just need some changes to make my blog better. After googling, I found jekyll, which is a simple, blog aware, static site generator, that means no databases and much less resources wanted, sounds great.&lt;/p&gt;

&lt;h2&gt;Build a Blog by Jekyll&lt;/h2&gt;

&lt;p&gt;Then I began to build the new blog system by jekyll two weeks ago. It's really easy to install and use, check the document &lt;a href="https://github.com/mojombo/jekyll"&gt;here&lt;/a&gt;. As you know, I'm a developer, of course I install the pygments for code highlight. But there are several limitations for the default jekyll.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;no category section on sidebar.&lt;/li&gt;
&lt;li&gt;no archive section on sidebar.&lt;/li&gt;
&lt;li&gt;no categroy page, which lists the posts in that category.&lt;/li&gt;
&lt;li&gt;no monthly archive page, which list posts by month.&lt;/li&gt;
&lt;li&gt;no comments, yep, it generates a static website.&lt;/li&gt;
&lt;li&gt;&lt;strike&gt;can't display liquid codes on post.&lt;/strike&gt;(Use literal tag to display liquid codes)&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Like rails, jekyll supports plugins and extensions so that we can extend it as we want. Originally I planed to host my blog on github, but I found github doesn't support any plugins and extensions, it only supports the default official jekyll. Bad news, I have to host it on my own server with jekyll extensions, it's not a big problem.&lt;/p&gt;

&lt;p&gt;The best extesion of jekyll I found is &lt;a href="https://github.com/rfelix/jekyll_ext"&gt;jekyll_ext&lt;/a&gt;, it provides a really flexible way to extend jekyll. The author also shares his jekyll &lt;a href="https://github.com/rfelix/my_jekyll_extensions"&gt;extensions&lt;/a&gt; using jekyll_ext. I forked the &lt;a href="http://disqus.com"&gt;extensions&lt;/a&gt; to fix the generation of archive page and add the archive section on sidebar.&lt;/p&gt;

&lt;p&gt;OK, let me show how to fix the above limitation with my forked extension.&lt;/p&gt;

&lt;p&gt;1. category section on sidebar.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  {% for category in site.categories %}
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/categories/{{category | first}}&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{category | first}} ({{category | last | size }})&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  {% endfor %}
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;2. archive section on sidebar.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  {% for monthly_archive in site.monthly_archives reversed %}
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ site.baseurl }}/{{ monthly_archive.url }}&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ monthly_archive.name }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; ({{ monthly_archive.posts | size }} posts)
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  {% endfor %}
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;3. category page, add a layout &lt;code&gt;category_index.html&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;---
layout: default
---

&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;page-title&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Category Archives:
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/categories/{{page.category}}&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{page.category}}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ol&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;archive&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
{% for post in site.categories[page.category] %}
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;excerpt&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;strong&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;entry-title&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ post.url }}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ post.title }}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bookmark&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ post.title }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;date small&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;abbr&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;published&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ post.date }}&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ post.date | date_to_string }}&lt;span class="nt"&gt;&amp;lt;/abbr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;alt-font&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Posted in&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;
        {% for category in post.categories %}
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/categories/{{ category }}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ category }}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;category tag&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ category }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        {% endfor %}
      &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;comments-link&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;{{post.url}}#disqus_thread&amp;#39;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Comments&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
{% endfor %}
&lt;span class="nt"&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;4. monthly archive page, add a layout &lt;code&gt;archive_monthly.html&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;---
layout: default
---

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{ page.month | to_month }} {{ page.year }}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ol&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;archive&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {% for d in (1..31) reversed %}
    {% if site.collated_posts[page.year][page.month][d] %}
      {% for post in site.collated_posts[page.year][page.month][d] reversed %}
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;excerpt&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;strong&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;entry-title&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ post.url }}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ post.title }}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bookmark&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ post.title }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;date small&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;abbr&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;published&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ post.date }}&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ post.date | date_to_string }}&lt;span class="nt"&gt;&amp;lt;/abbr&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;alt-font&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            Posted in&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;
            {% for category in post.categories %}
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/categories/{{ category }}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ category }}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;category tag&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ category }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
            {% endfor %}
          &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;comments-link&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;{{post.url}}#disqus_thread&amp;#39;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Comments&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      {% endfor %}
    {% endif %}
  {% endfor %}
&lt;span class="nt"&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;5. comments, hmmm...it's impossible for jekyll to provide comments functionality, but I guess you know the web service &lt;a href="http://disqus.com"&gt;disqus&lt;/a&gt; which provides an online comment system. You can get two javascripts after you creating an forum on disqus, one for posting/displaying comments, the other is to dispaly comments count for each post. The following is the javascript to post/display comments.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;disqus_thread&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;richard-huang&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;http://www.huangzhimin.com/2011/01/20/migrate-custom-blog-to-jekyll-and-disqus/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dsq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.disqus.com/embed.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;head&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dsq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&lt;/span&gt;Please enable JavaScript to view the &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://disqus.com/?ref_noscript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;comments powered by Disqus.&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://disqus.com&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dsq-brlink&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;blog comments powered by &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;logo-disqus&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Disqus&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And the javascript to display comments count.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;richard-huang&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/javascript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;disqus_shortname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.disqus.com/count.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;HEAD&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;BODY&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}());&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strike&gt;
6. can't display liquid codes. I found this limitation while I'm writing this post, it's impossible to display raw liquid codes, as liquid always try to execute each liquid code. I have to write a custom tag raw to solve this issue.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Jekyll&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Raw&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;Liquid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Block&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@nodelist&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
      &lt;span class="vi"&gt;@nodelist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;

      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shift&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;IsTag&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="no"&gt;FullToken&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;block_delimiter&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="vg"&gt;$1&lt;/span&gt;
              &lt;span class="n"&gt;end_tag&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="vi"&gt;@nodelist&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
          &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;SyntaxError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Tag &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39; was not properly terminated with regexp: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;TagEnd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &amp;quot;&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="vi"&gt;@nodelist&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="c1"&gt;# Make sure that its ok to end parsing in the current block.&lt;/span&gt;
      &lt;span class="c1"&gt;# Effectively this method will throw and exception unless the current block is&lt;/span&gt;
      &lt;span class="c1"&gt;# of type Document&lt;/span&gt;
      &lt;span class="n"&gt;assert_missing_delimitation!&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="ss"&gt;Liquid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;raw&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;Jekyll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So you can use the raw tag to escape all the liquid codes as you want.
&lt;/strike&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can get the source code of my blog system on &lt;a href="https://github.com/flyerhzm.github.com"&gt;github&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;Migrate Legacy Data&lt;/h2&gt;

&lt;p&gt;OK, the new blog system is complete, but what about the old blog posts
and comments? I want to migrate them to the new system.&lt;/p&gt;

&lt;p&gt;I'm a developer, so it's not too difficult for to migrate old data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Migrate old posts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like the common blog system, the old post is saved as html format. After working on several projects on github, I start to love markdown, so I decide to convert all the old html posts to markdown format. There is a project named &lt;a href="https://github.com/xijo/reverse-markdown"&gt;reverse-markdown&lt;/a&gt; to do this job, I also forked it to handle code highlight (before I used syntaxhighlighter, now is &lt;code&gt;{% highlight language %}...{% endhighlight %}&lt;/code&gt;), here is the &lt;a href="https://gist.github.com/788039"&gt;script&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then I began to migrate old posts&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rtranslate&amp;#39;&lt;/span&gt;

&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;temp&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;translated_title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Translate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;CHINESE_SIMPLIFIED&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ENGLISH&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;%Y-%m-%d&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;-&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;translated_title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameterize&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.markdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;w+&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;
&lt;span class="sh"&gt;---&lt;/span&gt;
&lt;span class="sh"&gt;layout: post&lt;/span&gt;
&lt;span class="sh"&gt;title: #{post.title.gsub(&amp;quot;&amp;amp;#65281;&amp;quot;, &amp;quot;！&amp;quot;).gsub(&amp;quot;&amp;amp;#65292;&amp;quot;, &amp;quot;，&amp;quot;)}&lt;/span&gt;
&lt;span class="sh"&gt;categories:&lt;/span&gt;
&lt;span class="sh"&gt;- #{translate.t(post.category.name, &amp;#39;chinese_simplified&amp;#39;, &amp;#39;english&amp;#39;)}&lt;/span&gt;
&lt;span class="sh"&gt;---&lt;/span&gt;
&lt;span class="sh"&gt;#{ReverseMarkdown.new.parse_string(post.body)}&lt;/span&gt;
&lt;span class="no"&gt;    EOF&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I run the above codes in rails console, the above codes translate the post title and category name from Chinese to English, convert the body of post from html to markdown, and then save them under temp directory.&lt;/p&gt;

&lt;p&gt;After running the codes, there are a lot of posts generated under temp directory, I just copy them to the _post directory in the new blog system, then the posts migration is complete. Cool!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Migrate old comments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Migrating comments is a bit difficult, it takes me a few days to play with disqus api. Luckily disqus provides a api console, I really like it.&lt;/p&gt;

&lt;p&gt;The following codes are what I used to migrate comments to disqus.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rest_client&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;json&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;open-uri&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;disqus_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://disqus.com/api/3.0&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;secret_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;your secret key&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;current_blog_base_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://www.huangzhimin.com&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;RestClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;disqus_url&lt;/span&gt;

&lt;span class="n"&gt;forum_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;richard-huang&amp;#39;&lt;/span&gt;

&lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;translated_title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Translate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;CHINESE_SIMPLIFIED&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ENGLISH&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;%Y/%m/%d&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;translated_title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameterize&lt;/span&gt;
  &lt;span class="n"&gt;post_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;current_blog_base_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Richard Huang - &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;thread_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/threads/list.json?api_secret=&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;amp;forum=&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;forum_id&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;response&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;thread_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;link&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;post_url&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;thread_id&lt;/span&gt;
      &lt;span class="n"&gt;request_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:forum&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;forum_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:url&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;post_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/threads/create.json?api_secret=&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;response&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;thread_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;request_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:thread&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:author_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:date&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;request_body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:author_email&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blank?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;anonymous@gmail.com&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;request_body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:author_url&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;present?&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/posts/create.json?api_secret=&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;code&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Success: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;FAIL: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Rescue: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;post_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The aboved codes are also run in rails console, it works as follows.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;checks if the new post url existed.&lt;/li&gt;
&lt;li&gt;if so, it reads or creates a thread, one thread on disqus is corresponding to one post url in blog system.&lt;/li&gt;
&lt;li&gt;then create a post on disque, one post on disqus is corresponding to one comment in blog system.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;There is a problem, in disqus, email of comment author can't be empty, but in my old blog system the email of comment user can be empty, so I have use "anonymous@gmail.com" instead. This is the only limitation when I migrate old comments.&lt;/p&gt;

&lt;p&gt;Everything works well. I love my new blog system.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/0dbSks_0xbo" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Construct Nested Hash in Ruby</title>
   <link href="http://www.huangzhimin.com/2011/01/18/construct-nested-hash-in-ruby/" />
   <updated>2011-01-18T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/01/18/construct-nested-hash-in-ruby</id>
   <content type="html">&lt;p&gt;I just received a post request on rails-bestpractices.com from hlxwell, he recommend "Nested hash simple initialization."&lt;/p&gt;

&lt;p&gt;Change From&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;cache_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;cache_data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;cache_data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;cache_data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;cache_data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;cache_data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;To&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;cache_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;k4&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h4&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k4&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;cache_data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Frankly speeking, I don't agree with him.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;I don't think he needs the too much level nested hash, he may reconsider his design of data structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If he really needs such nested hash, he should use the more graceful way instead&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;leet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;leet&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="n"&gt;cache_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;leet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cache_data&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/FAp9Tcjj47M" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Use different mongodb instances in mongoid</title>
   <link href="http://www.huangzhimin.com/2011/01/14/use-different-mongodb-instances-in-mongoid/" />
   <updated>2011-01-14T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/01/14/use-different-mongodb-instances-in-mongoid</id>
   <content type="html">&lt;p&gt;By default, we save all the collections in one mongodb instance, or replicate/shard all of them into different mongodb instances. But what if saving a special collection into one mongodb instance, and the other collections into the other mongodb instance?&lt;/p&gt;

&lt;p&gt;This is what I need to do with a mongoid project several weeks before.  In common we just define a mongodb instance in the &lt;code&gt;config/mongoid.yml&lt;/code&gt;, yep, you can define only one instance for one environment just like activerecord, the define the different mongodb instance in model.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ad&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="ss"&gt;Mongoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Document&lt;/span&gt;

  &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;Mongoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Collections&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Master&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;AD_DB_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;AD_DB_PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;AD_DB_NAME&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="no"&gt;AD_COLLECTION_NAME&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;change the &lt;code&gt;AD_DB_HOST&lt;/code&gt;, &lt;code&gt;AD_DB_PORT&lt;/code&gt;, &lt;code&gt;AD_DB_NAME&lt;/code&gt; and &lt;code&gt;AD_COLLECTION_NAME&lt;/code&gt; with your mongodb configuration, the following is my configuration,&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;AD_DB_HOST&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;
&lt;span class="no"&gt;AD_DB_PORT&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;27018&lt;/span&gt;
&lt;span class="no"&gt;AD_DB_NAME&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ad_developmet&amp;#39;&lt;/span&gt;
&lt;span class="no"&gt;AD_COLLECTION_NAME&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ads&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and it would be better move these configurations into a config file.&lt;/p&gt;

&lt;p&gt;Now when saving or reading the ads collection, it uses &lt;code&gt;localhost:27018/ad_development&lt;/code&gt;, and the other collections use &lt;code&gt;localhost:27017/project_development&lt;/code&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/Oliab7qYtqY" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>Drop tmp collections in Mongodb</title>
   <link href="http://www.huangzhimin.com/2011/01/06/drop-tmp-collections-in-mongodb/" />
   <updated>2011-01-06T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2011/01/06/drop-tmp-collections-in-mongodb</id>
   <content type="html">&lt;p&gt;I'm trying mongodb map/reduce functionality with mongoid these days. I find there is a &lt;code&gt;tmp.mr.mapreduce_ddd_ddd&lt;/code&gt; collection created after each map/reduce operation, it's ok that these tmp collections are used to hold output of map/reduce operation.&lt;/p&gt;

&lt;p&gt;From mongodb document, it's said the temp collections will be cleaned up when the client connection closes or when explicitly dropped. But I never see these temp collections are dropped, when I print &lt;code&gt;show collections&lt;/code&gt;, there are too many temp collections annoyed me, I decided to drop these temp collections explicitly.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namespaces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/tmp.mr/&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getMongo&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It finds all the namespaces whose names contain tmp.mr, if so, drop the collections.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/q_xiekOLITA" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>JQuery Plugin Template</title>
   <link href="http://www.huangzhimin.com/2010/12/20/jquery-plugin-template/" />
   <updated>2010-12-20T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2010/12/20/jquery-plugin-template</id>
   <content type="html">&lt;p&gt;JQuery is one of the most important javascript framework I used, besides
default jquery apis, I use a lot of jquery plugins to improve the web
interactive, such as auto complete, dropdown menu, chart and so on.&lt;/p&gt;

&lt;p&gt;I will be likely to write some jquery plugins or hack others' jquery
plugins, The following is the template for a jquery plugin that I
learned from "jQuery 1.4 Plugin Development"&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pluginName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;......&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;......&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;the structure &lt;code&gt;(function($) {...})(jQuery)&lt;/code&gt; can protected the conflict
of $ sign which is used by other javascript framework.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$.extend({}, defaults, options)&lt;/code&gt; provides a flexible way to tweak your
plugin, options can override defaults, but not change the value of
defaults.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;return this.each(function() {...})&lt;/code&gt; promises your plugin functions are
chainable, like the default jquery apis.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/zbgi0eq1AKM" height="1" width="1"/&gt;</content>
 </entry>
 
 <entry>
   <title>盛大创新院分享</title>
   <link href="http://www.huangzhimin.com/2010/08/12/innovation-institute-grand-share/" />
   <updated>2010-08-12T00:00:00+08:00</updated>
   <id>http://www.huangzhimin.com/2010/08/12/innovation-institute-grand-share</id>
   <content type="html">&lt;p&gt;昨天去盛大创新院做分享，主要是介绍我目前正在做的网站，&lt;a href="http://rails-bestpractices.com"&gt;rails-bestpractices.com&lt;/a&gt;&lt;/p&gt;

&lt;div style="width:425px" id="__ss_4946169"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/flyerhzm/rails-bestpracticescom" title="Rails bestpractices.com"&gt;Rails bestpractices.com&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse4946169" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=rails-bestpractices-com-100811082516-phpapp02&amp;stripped_title=rails-bestpracticescom&amp;userName=flyerhzm" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse4946169" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=rails-bestpractices-com-100811082516-phpapp02&amp;stripped_title=rails-bestpracticescom&amp;userName=flyerhzm" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/flyerhzm"&gt;Richard Huang&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;


&lt;p&gt;盛大创新院还是很不错的地方，只可惜昨天他们一些做rails的牛人都在开会，失去了一个被拍砖的机会&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/richard-huang/~4/XHRd42yviJc" height="1" width="1"/&gt;</content>
 </entry>
 

</feed>
