Official Forumwarz Blog

The official blog for the web game, Forumwarz

Posts Tagged ‘code

Interview on Building Browsergames

There’s a short interview with me up on Building Browsergames. I think it’s a promising site that’s geared towards helping out develoepers who are interested in building their own games like Forumwarz.

(I’ve also agreed to do a Forumwarz “Post-Mortem” for them. It should be live shortly and I’ll probably cross-post/link it on this blog too.)

Advertisement

Written by eviltrout

February 15, 2009 at 4:22 PM

Posted in Home, News

Tagged with ,

Flood Detection, Rails and Memcached.

Note: The article is about the code behind Forumwarz for those who are interested in such things. If people like it I might write more like it!

Flooding is what happens when a user submits data repeatedly to your server. A good example would be a user who repeatedly posts comments on your message board.

Sometimes it’s done innocently: a user hits the submit button 5 times impatiently during a bit of server lag, only to find their post went through 5 times.

Other times it’s nefarious and deliberate: a user creates a wget script to just post garbage over and over.

Not only can flooding be taxing on your server, but it can fill up your site with so much garbage that it will put other users off. Obviously it’s something you want to get rid of!

A simple and effective strategy: the cool down period

You can assign a “cool down” period, where a user is barred from submitting data until the period ends. The length of the cool down is largely up to you, based on what you consider normal submission processes. I’ve found 30 seconds seems to work in most cases.

An obvious way to do this would be to add a timestamp column to the row you insert (which is always a good idea anyway!). Then, when posting, query the table you’re about to insert into for anything from that user within that cooldown period. If any rows are found, don’t allow the insert.

For most sites, implementing something like this will work great. However, and I cannot stress this enough, make sure you have a good database index on your timestamp column. If you do not, every insert will result in a table scan and your site performance will be terrible.

A memcached implementation

I got this idea from the Memcached FAQ and it works splendidly!

Memcached allows you to set an expiry for any key you set. So, instead of using the timestamp column in the database, you can simply set a key in memcached with an expiry of your cool down period. Then, when you are about to insert, check to see if the key exists in memcached. If it does, don’t insert. If it doesn’t, insert your row and then add the key there.

It is incredibly fast, and it doesn’t matter if your tables are indexed on the date column or not. In fact, since it’s not tied to your database at all, you can do flood prevention on anything you want (sending emails, real time chat, etc)!

Adding it to ActiveRecord

I have created a custom validation method in ActiveRecord for flood protection. It can be attached to any model using the following simple syntax:

prevent_flood 30.seconds, :user_id

The first parameter is the length of the cooldown period. The second parameter is the column in the model that uniquely identifies the user. In this case, it’s a user_id column.

The implementation looks like this:

      def prevent_flood(cooldown, field)
        validates_each(field, :on => :create) do |record, attr_name, value|
          cache_key = "flood:#{record.class.name}_#{record[field]}"

          unless CACHE_ME.get(cache_key).nil?
            record.errors.add_to_base("You're posting too often. Slow down!")
          end
        end

        after_create do |record|
          CACHE_ME.put("flood:#{record.class.name}_#{record[field]}", "F", cooldown.to_i)
        end
      end

CACHE_ME is an abstraction I wrote to use Memcached from ruby. It it initialized to connect to memcached when rails starts up, and can be replaced with however you personally connect to Memcached fairly easily. The get method returns nil if the key isn’t there, and the put method sets a key to be the value “F” with an expiry of cooldown seconds. It doesn’t really matter what value you put in the cache, I just chose F for flood, and because it’s one character long.

I know personally flood protection is something that I never really implement until it becomes a problem, and one of the reasons was that it was a pain in the butt to code for every model. However, with this interface, I am now using it on all new code I’m writing from the beginning. The overhead is minimal, and it can really save your butt down the line!

Written by eviltrout

April 16, 2008 at 5:56 PM

Posted in Coding, Home

Tagged with