Latest Posts

"Zecco Zimulator" Launched

Posted on Dec 12, 2008 by Paul Canavese

/content/blog_articles/8/30.gif
The fantasy financial trading site that I developed for PTrades has finally launched as the Zecco Zimulator

The site allows you to open as many accounts as you want for free, filled with $100,000 of play money.  You can then trade stocks, stock options, and futures and gauge your success (before trying with real money).  You can form and join teams and compete to rise up the high score boards.  All this can be done on the main site or on Facebook, so your friends can read about your trading success in your news feed.

Check it out yourself here.


Leaderboards and Fragment Caching

Posted on Sep 25, 2008 by Paul Canavese

/content/blog_articles/7/24.png
When I worked as a server engineer for a game company, I spent some significant time dealing with leaderboards (a.k.a. high score lists).  And lo and behold, when I started consulting and was asked to build a fantasy stock/options/futures trading site, there I was coding leaderboards again.

With a small number of people, of course, it's not a big deal to generate leaderboards on the fly.  But if your site is lucky enough to become successful, you don't want your database queries to bring your site to its knees.  Things also get more complicated if you want to implement "around me" leaderboards which show the current user's position with a few people above and below.

Leaderboard Tables

The first step is to create separate tables specifically for the leaderboards, rather than expecting to use other tables (e.g. user, team, or network tables).  Periodically, do a query from the source table sorting by score.  After clearing out the leaderboard table, the results can be inserted with the primary key being position in the table.  This makes it easy to look up a particular user to find their position and then do an "around me" query.

Doing a delete all from the table followed by an insert (all within a transaction) will work up to a certain point.  But when you're pushing hundreds of thousands of users that will likely be a performance issue.  An alternate approach is to create a new temporary table, do the inserts, then—in a new transaction—delete the old table and rename the temporary table to replace it.

Fragment Caching

I wanted to share one other trick I was able to use in my latest project.  I wanted to cache the HTML fragments showing the leaderboards, but also wanted the current user to see themselves highlighted in the table (meaning the view would look different to different users).  That would seem to eliminate the possibility of global caching.

But the only difference across users is a CSS styling, and one that could be controlled in a dynamic way.  So what I did was assign a CSS class to each row named to the user id.  For example:

<tr class="user_2635">...

Then, in the dynamic portion of the generated page I added a style definition targeting the current user:

tr.user_2635 { background-color: #f4f1ac; }

Then the same HTML table can be cached and shared across all users.


"AvaPeeps: FlirtNation" Launched

Posted on Sep 12, 2008 by Paul Canavese

/content/blog_articles/6/25.png

I spent a good portion of my life, while I was employed by Digital Chocolate, working on a game called AvaPeeps:FlirtNation.  Initially, it was only targeted at the mobile phone market, but it has just been launched as a beta release as a web game.

The game allows you to design an avatar character and send him/her out on dates with other people's avatars.  You can send your avatar to different locations to hang out and meet other avatars, coach him/her in what to do on dates, and exchange messages with other peeps.

It was a fun and challenging application to work on, and it will be interesting to see how it does with a web audience.  As a member of the team, I spent a lot of time figuring out how to get it to scale up to support millions of users.  Hopefully that work will be put to the test.

It's free, so check it out!


Spam Woes

Posted on Aug 26, 2008 by Paul Canavese

I host my own email (and that associated with all the domains I manage), and therefore have to deal with the issue of spam. My spam filter currently catches between 500 and 600 messages a day, and misses around 50 spam messages.

While it is pretty accurate, I do feel I need to personally review the spam mailbox before deleting messages, as somewhere around 1 in 5000 messages tagged as spam are legitimate.  I've taken different approaches to this review, but currently sort all messages by subject and then can quickly page through messages with identical or similar subject lines.  For example, currently in the hopper:

  • Two pages of "Angelina Jolie"
  • Four pages of "Britney Spears"
  • Twelve pages of debt ("consolidation, "loans", etc.)
  • Three pages of replica watches and handbags
  • Seven pages of "Solution for your sexual life"

I have been using SpamAssassin for a number of years and have been quite happy with it.  It would presumably be even more effective if I had a more current version of the filters.

But I am getting very tempted to move to a challenge response system, as annoying as they are.  I'm just spending too much of my precious time combing through the spam box.


Rails Generated Methods

Posted on Aug 23, 2008 by Paul Canavese

One of the downsides to Rails' automatic method generation is that sometimes it bites you in the butt.

I've been tearing my hair out trying to figure out why ActionView::Helpers::AssetTagHelper.image_path was behaving strangely all of a sudden.  It didn't help that I had a boatload of local changes and there were multiple layers causing problems.  I am using the Rails Engines plugin, which was alias_method_chaining that method, and I thought the problem was in the aliasing.  But the other AssetTagHelper methods (stylesheet_link_tag, javascript_include_tag, and image_tag) were all correctly aliased and working fine.

It turns out that when I created a RESTful route for my new Image ActiveRecord class that it automatically created an image_path method, overriding the normal one.

I'm going to go look for some glue to see if I can get this hair to stick back on my head.