Saturday, May 18, 2013

The Lean Startup, My Real Practice (3)

There has been about 1 year since my last blog about The Lean Startup in April 2012. What happens during this year?
  • My majority responsibility changed from desktop software side to cloud services. It is perfect to start to working on new technologies. The bad thing is, I'm really busy and exhausted, and din't have time to take care of PomodoroApp. I spent a lot of time learning the new technologies, programming languages, existing design for the system on the new division. Everything on PomodoroApp has been stopped for 8 months until the last Christmas.
  • My baby was born. Kelly and I are very happy to have our first baby. Kelly spent a lot of time on taking care of the baby, and didn't have enough time on marketing/customer services/ux design.
  • The traffic of the website of PomodoroApp is increasing every month with a very stable speed. You can see the traffic report by google analytics below:

  • I'm getting more and more familiar with JavaScript and cloud technologies. I'm now confident with cloud technologies.
By the end of 2012, I can have 2 weeks vacation. I decided to leverage the 2 full weeks for a brand new version of PomodoroApp, with following majority changes:
  • Programming Language and Libraries: The version 1.x and 2.x are built based on Qt, a C++ library. I used the old but mature UI technology, CSS style, to create beautiful UI controls. Qt and C++ are powerful and fast on every platform. I also write some code with Objective-C on Mac and COM interface on Windows to handle some OS specific features, e.g. dockbar icon. With the recently experience, I'll shift to TideSDK and ExtJS. With TideSDK I can create beautiful and unique desktop apps using web technologies. ExtJS is a web application library with a lot of samples, but may need more time to learn. Other libraries like jQuery UI has been considered also, but not has necessary features in place for me.
  • Data Synchronization Service: because PomodoroApp is cross platform, currently on Windows/Mac/Linux, and will support iPhone/Android also. There is no reason not to synchronize data across devices. I use node.js + MongoDB as backend, and host servers on Linode, Digital Ocean, and Windows Azure. That's really strange infrastructue architecture. The puporse is to reduce the cost, and try servers from different providers.
  • Mobile Platform: Appcelerator Titanium is really fantastic for cross platform mobile development. Again, need some time to learn at the beginning.
So with ideas on every aspect settled down, I start to work with full of enery. Because I didn't always have this kind of long vacation for my own projects. I started the first release of PomodoroApp at the end of 2011. The main reason is that I have 2 weeks vacation, and I need to take the advantage of the long vacation. I have to admit that I also made some bad decisions when building the idea. I've listed some topics, and will have some blog posts to share my experience and lessons learned in future. So, what's the stuff about "Lean Startup"? This is a summary of what happens in the past year. I'll start a new blog post about the detail of this new version.

Sunday, May 12, 2013

A Story of "Design for Failure"


When we come to the era of cloud computing, what's the most important factor you can imaging for the cloud computing? You may think of scaling. It could be, scaling is very important when your business getting bigger and bigger. You may think of backup, it always should be. You may also think of programable computing resources. That's a really important concept from AWS. Machine is programable, you can programmatically add or delete a  machine within seconds, instead of purchasing from vendor and deploy it to data center. You can allocate a new reliable database, without dependency on operations team. However, as a startup, my business is starting from scratch, and I do everything myself. In my practice, "Design for Failure" is really the top priority at the very beginning.

As AWS providing EC2, and other vendors providing VPS, it would be a common sense to use VPS instead of building your own data center when you are not so big. Scaling is not so important because I'm still very small, limited machines are enough to support scale of current users. But I do designed for scaling in future. Design for failure? Yes, I have considered, but not so seriously. My VPS provider, Linode, claimed a 99.95% availability, and Linode has very good reputation in this industry. I trust them.

Some background around my online service. I released a new version of desktop application PomodoroApp at the end of 2012, and support data synchronization across computers. User will rely on my server to sync data. It's yet another a new service on Internet, no one knows it. I'm not sure tomorrow it will be only 1 new users or 1,000 new users. Although I designed a reliable and scalable server architecture, I applied a minimum viable architecture for servers in order to reduce the cost. Perhaps nobody will use the service in next week. 2 web servers, one to host my website, and another to host a node.js server for data synchronization. It provide only rest services, I'll call it sync server. 1 MongoDB database server instance. Each one can be a single point of failure. It's acceptable if I have 99.95% availability. My sync server is in a very low load, so I configured the sync server to be the secondary of MongoDB replica set. The server code also support accessing data from replica set.



Everything ran very well in the coming 2 months. I keep improving the server, adding new features. Users came to use my service from google, blog, Facebook, twitter, and increased with a stable speed. When I have new code, just need 1 seconds to restart service. February 17th, 2013, for an unknown reason, database server is out of service. Nobody knows the reason, Linode technical support managed to fix the issues. When database server was down, the secondary database on sync server became primary, and all data read/write switched to database on my sync server automatically, this may take 1 minute, depending on the timeout settings. So the outage of the database server has no impact to my sync service. 

However, I'm just lucky for the incident of Feb 17. Just 3 days later, my sync server is down, and I even cannot restart the server from Linode managed console.  This took 55 minutes. I got alerts from monitor service pingdom, also from customers report. This is the first lesson. So the single point of failure does happen. I decided to add more sync servers. Consequently, a load balance server is necessary for 2 sync servers. In addition, I added the 3rd replica set which has 1 hour delay from primary server. In case there are any data broken, I can recover it from the backup server. You may ask why 1 hour delay instead of 24 hours. Ideally there should be multiple delayed replica set servers. In my production environment, user count is still small, and there is no necessary for sharding so far. But my new features, or my changes to existing code is only tested on dev environment. When I deployed it to server, it may make damage to server. I need a backup plan for this case. Even there are still SPOF, it 's much better:)

The real disaster happened in May 11, I am going to deploy new version which resolved some issues on database. The new version handled index creation on database. I use a web based admin tool to manage my MongoDB instances. When I connect production database for final release testing, I happened to found a duplicated index on the collection. I'm not sure why this happen, so I deleted one on admin tool. The tool reported that 2 indexes are both deleted. Later when I continue my testing and try to sync data to server. I got the error that failed to commit to database. This never happens before. Then I use MongoDB console to check the collection. What made me surprising is, the whole collection is lost, neither to be created again. I shutdown the MongoDB server, and then try to restart it. Failed! The database log indicates "exception: BSONObj size: 0 (0x00000000) is invalid. Size must be between 0 and 16793600(16MB) First element: EOO". Googling the exception does not help much. Oh my, finally I have to recover the database. Fortunately I have a replica set which have realtime mirror for the database, and another replica set which has 1 hour delay for the database. I spent about 2 hours on fixing the issue, but my sync service is still online and functioned well. Because I have "stepDown" my primary and the secondary is now work as primary. Doing these troubleshooting does not hurt my online service. MongoDB really did an excellent job on the replica set pattern.

Initially I decided to recover the database from the replica set which has 1 hour delay. But it's in another datacenter, I use scp to copy data file, only 1.7M bytes/seconds, I have 9G bytes data in total. That would spent a long time for copying. Then I checked the new primary database, fortunately found that the new primary(the old secondary) is in good shape, the data file does not broken. Then I stopped the primary database, and spent about 2 minutes to copy all the files with a 29M bytes file transfer speed within the same datacenter. Again, it's still a very small business. 2 minutes outage is acceptable, because my client software support offline mode, it has local database, and can work at the place without Internet. When the network is available, it will sync to server. Some users even disabled the sync feature because they don't what to upload any data to server. After all files are copied, I restart MongoDB. It took several seconds to recovery the uncommitted data from oplog, and try to duplicate from the primary server. Everything works well now. MongoDB rocks!

Even I have the ultimate backup plan designed and tested on my client software, it still make me tense very much. Actually my  backup plan is, if the whole database is lost, I can still recover all the data. My client software supports offline mode, it duplicated all the data for the user. Automatic data recovery from user's machine to server has already been there. 

This story is the first real disaster for me so far. I respect VPS provider Linode, and respect to software companies who provided linux server, node.js, MongoDB. But it's really a must to keep the "design for failure" the top priority even you are very small. The hardware may be outage, the software may have bugs, the IO or the memory may be corruption. Hackers may need your server. People may say, the only thing that never change is change. My lesson is, the only thing that never failure is failure. Without these lessons, "Design For Failure" would never have so tremendous impact for my future design. 

Wednesday, November 7, 2012

RockMongo: MongoDB Client on Mac OSX Lion

On Mac OSX Lion, I use MongoHub as the client tool to view and display my mongodb. MongoHub has native UI for Mac, and provided functionalities for most of my operations. However MongoHub is a little bit buggy and sometimes crashes. On my apache server, I use RockMongo, an excellent Mongo administrator site to manage my mongodb. Here is some steps for me to lunch RockMongo on my Mac OSX Lion(10.7). It runs perfectly!


  1. Apache2 and PHP5 has already been installed on Mac OSX. You just need to enable it.
    • Enable Apache2:   
    install apache2 on mac osx
    • PHP is disabled on apache2 by default, you need to enable it manually: open apache2 config file with command "sudo vi /etc/apache2/httpd.conf", find the line "LoadModule php5_module libexec/apache2/libphp5.so" and remove the "#" at the beginning to enable php5 module.
  2. Install mongo php driver
    • You may need to firstly install the php tool "pecl"
      • cd /usr/lib/php
      • sudo php install-pear-nozlib.phar
      • Edit/etc/php.ini and find the line: ;include_path = ".:/php/includes" and change it to:
        include_path = ".:/usr/lib/php/pear"
      • sudo pear channel-update pear.php.net
      • sudo pecl channel-update pecl.php.net
      • sudo pear upgrade-all
    • Then run command "sudo pecl install mongo". Make sure you xcode has been installed correctly. pecl will download mongo php driver source code and build it. (The precompiled mongo.so may not work on your machine. So you have to use pecl to install the driver.)
    • run command sudo vi /etc/php.ini to open php.ini for editing, if you /etc/php.ini does not exist, copy /etc/php.ini.default to /etc/php.ini, add "extension=mongo.so"
  3. Download RockMongo source code, and copy to your "computer website folder"
  4. You need to run "sudo apachectl restart" to restart apache2 server.
  5. Login to http://localhost/rockmongo, with user/password as "admin/admin". Then you should see  RockMongo as follows:

Saturday, November 3, 2012

Grid to Excel export plugin for ExtJS 4

ExtJS has an excellent component Grid Panel to display well structured data. The Grid component may be able to fulfill customer's requirements in most of the cases, but once customer need to do customized calculation on the grid, the may need to export the grid data to their favorite tool, for example, Microsoft Excel, for further process.

With some investigation on internet, this requirement actually existed for a long time, there are 2 main thread on sencha forum:
The discussions have been there for 3 years, but neither of the works well so far, either because of compatibility issues on Ext.JS 4 or bugs in them. I'm not going to discuss more about details, but summarized the 3 ways:
  1. Leverage document data location: set document URL to BASE64 encoded data like: data:application/vnd.ms-excel;base64,... 
    • Pros: pure JavaScript side, no dependency on any 3rd party tools or objects.
    • Cons: the download file cannot be customized, and there are some limitations on encoding UTF-8 to BASE64
  2. Send request to server and generate data stream with specified file format on server.
    • Pros: still pure JavaScript on client side, and developer can choose the file name.
    • Cons: need some work on server side, the code here made it works on JSP.
  3. Use flash Downloadify to generate a file on the fly.
    • Pros: No server side work, user can choose the file name just like a normal download.
    • Cons: depends on flash object.
My version of Ext.ux.Exporter  actually forked the version who use Downloadify, and fixed some defects to make it works on ExtJS 4.1.1a. Here is the code to use it:

  • include script files in html file:
<script type="text/javascript" src="/extjs/src/ux/exporter/swfobject.js"></script>
<script type="text/javascript" src="/extjs/src/ux/exporter/downloadify.min.js"></script>
  • in docedItems list of grid panel, generally on a toolbar, add an item as follows::
{
    xtype: 'exporterbutton'
}
In my implementation, I didn't take time to resolve the excel format issue, instead I use CSV format as default format. What I need is not to keep the exact style when load. I need further processing on data, and a CSV file can fulfill my requirements perfectly.

Monday, April 2, 2012

The Lean Startup, My Real Practice (2)

There have been two and a half months since My last blog post The Lean Startup, My Real Practice. Here is the notes about what what has happened in the past 75 days.
  • Released a new version in February 7, 22 days after I released the first version. The majority change is the refinement of user interface for lists of to do today, task list, and interruptions. Here is the post I asked in stackoverflow.
  • Made a promotion on Giveaway of the Day in February 29, provided a one-day promotion for free of charge.
  • Made a promotion on bits dujour, provided a one-day promotion for 50% discount.
Here is the statistics for traffic:
  • 11,175 Visits, there are half of the visits are from the Giveaway of the Day promotion. Recently the traffic getting stable to about 790 visits per week.

  • 44.81% Bounce Rate, I hope I can improve this number, but in future when I made efforts on SEO, I think this number should be higher than current.

  • Traffic source within the past 75 days:
    • 17.34% Search Traffic
    • 63.90% Referral Traffic
    • 18.56% Direct Traffic

  • 48.96% Search Traffic within recent 2 weeks. The traffic number is impacted a lot by GOTD promotion.
[caption id="attachment_63" align="aligncenter" width="743" caption="PomodoroApp Visits (weekly) from 01/15/2012 to 03/31/2012"][/caption]

About the revenue:

  • January: US$0.00
  • February: US$0.00
  • March: The first order happens in March 3. 2 order for organic traffic, and 17 orders from the promotion on bits dujour. The total sales is: US$249.94, and profit is $127.82. In addition, some profit from promotion on GOTD.
  • I'm not sure how much will get in April, but the sure thing is that there is no promotions in April. If you have any suggestions to have promotion options, please be sure to let me know.
What's I'm working in progress:
  • Redesign the user interface, propose the concept of "dashboard", tasks will be categorized by dashboard.
  • Refactor the code and prepare to integrate with Jira.
Something that I didn't do well and should be improved:
  • I spent too much time to review traffic statistics from google analytics. However, my most important task is to complete the new version as soon as possible.
  • I didn't take time to follow up when PomodoroApp appears on social networks or blogs, news software. I can find those site by looking for the emergency traffic from a referrer.
  • I didn't have a tight schedule or plan for my new release since February 7. However, when I release the first 2 versions, I made a very aggressive deadline and did my best to follow the date.
  • My fulltime daily job made me exhausted and I didn't have energy to continue to work in after work hours.

Saturday, March 31, 2012

An overview of Qt Quick 1.1 Components

I'm recently redesign the UI of PomodoroApp, and testing Qt Quick. I read through the tutorials and resources below:

  • Qt Essentials - Qt Quick for C++ Developers: 8 excellent slides for you to getting started with Qt Quick, and understand most of the features

  • QML Elements: Detailed description for grouped lists of QML elements as part of Qt Quick.

  • Some samples from Qt demo/examples declarative folders. You can build and run those features and see what QML can do.

While my purpose is to have a smooth and beautiful UI for my app on *desktop*, both Windows, Mac and Ubuntu. I may need to spend more time to learn QML and create basic elements for desktop widgets. Then I found some existing QML components here. I played for a while. Here is the screenshot for colibri, beautiful and has covered most of frequently used components.

[caption id="attachment_60" align="aligncenter" width="300" caption="Qt Quick QML Component "colibri""]Qt Quick QML Component "colibri"[/caption]

How to make debian package (.deb) with scripts

When I port my qt projects to Ubuntu/Debian Linux systems, I provided the compiled debian package, a deb file for users. I use a GUI debian package creator to make a deb package. Today when I browser qt-components, I got this bash script that can create deb file easily:


#!/bin/sh
MAKE='make -j4'

cd `dirname $0`

rm -rf debian
mkdir debian
cd debian

mkdir -p "usr/share/applications"

desktop_file_meenotes="usr/share/applications/meenotes.desktop"
echo "[Desktop Entry]" > "$desktop_file_meenotes"
echo "Version=1.0" >> "$desktop_file_meenotes"
echo "Name=MeeNotes" >> "$desktop_file_meenotes"
echo "GenericName=MeeNotes" >> "$desktop_file_meenotes"
echo "Comment=MeeNotes" >> "$desktop_file_meenotes"
echo "Exec=/opt/meenotes/MeeNotes" >> "$desktop_file_meenotes"
echo "Terminal=false" >> "$desktop_file_meenotes"
echo "Type=Application" >> "$desktop_file_meenotes"
mkdir -p DEBIAN

control_file="DEBIAN/control"

echo "Package: meenotes" > "$control_file"
echo "Version: 1.0" >> "$control_file"
echo "Architecture: all" >> "$control_file"
echo "Maintainer: Qt developers" >> "$control_file"
echo "Description: Qt components MeeNotes" >> "$control_file"

[ ! -f "../Makefile" ] && (cd ..; qmake)
(export INSTALL_ROOT=`pwd`; $MAKE install)

mkdir -p opt/meenotes

cp -ar ../MeeNotes opt/meenotes/
cp -ar ../resource opt/meenotes/resource

cd ..
dpkg-deb --build debian
mv debian.deb meenotes.deb