Skip to content

Commit

Permalink
doc updates
Browse files Browse the repository at this point in the history
  • Loading branch information
boxofrobots committed Nov 24, 2009
1 parent c32e438 commit 8dfb1c1
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 116 deletions.
98 changes: 3 additions & 95 deletions ATOMICITY.rdoc
Original file line number Diff line number Diff line change
@@ -1,98 +1,6 @@
= Redis Objects - Lightweight object layer around redis-rb you can also use in a model
= An Atomic Rant

Redis is great _not_ as a replacement for MySQL, but as a way to perform atomic
operations on _individual_ data structures, like counters, lists, and sets. People
that are wrapping ORM's around Redis are missing the point.

This gem, instead, provides atomic methods that you can use *with* your existing
ActiveRecord/DataMapper/etc models, or in classes that have nothing to do with an
ORM or even a database, but need support for high-performance atomic operations.

The only requirement Redis::Atoms has is that your class must provide an +id+ instance
method which returns the ID for that instance. ActiveRecord, DataMapper, and MongoRecord
all have id methods which are known to be suitable. Since +id+ can be anything as
far as Redis::Atoms is concerned, you can even write an +id+ method of your own that
just returns a string, or an MD5 of the name, or something else unique.

== Installation

gem install gemcutter
gem tumble
gem install redis-atoms

== Example

Somewhere in your app initialization

require 'redis'
require 'redis/atoms'
Redis::Atoms.redis = Redis.new(:host => 127.0.0.1, :port => 6379)

Model class:

class Team < ActiveRecord::Base
include Redis::Atoms

counter :drafted_players
counter :active_players
lock :reorder, :timeout => 5 # seconds
end

Using counters to handle concurrency:

@team = Team.find(1)
if @team.drafted_players.increment <= @team.max_players
# do stuff
@team.team_players.create!(:player_id => 221)
@team.active_players.increment
else
# reset counter state
@team.drafted_players.decrement
end

Atomic block - a cleaner way to do the above. Exceptions or nil results
rewind counter back to previous state:

@team.drafted_players.increment do |val|
raise Team::TeamFullError if val > @team.max_players
@team.team_players.create!(:player_id => 221)
@team.active_players.increment
end

Similar approach, using an if block (failure rewinds counter):

@team.drafted_players.increment do |val|
if val <= @team.max_players
@team.team_players.create!(:player_id => 221)
@team.active_players.increment
end
end

Class methods work too - notice we override ActiveRecord counters:

Team.increment_counter :drafted_players, team_id
Team.decrement_counter :drafted_players, team_id, 2

Class-level atomic block (may save a DB fetch depending on your app):

Team.increment_counter(:drafted_players, team_id) do |val|
TeamPitcher.create!(:team_id => team_id, :pitcher_id => 181)
Team.increment_counter(:active_players, team_id)
end

Locks with Redis. On completion or exception the lock is released:

@team.reorder_lock.lock do
@team.reorder_all_players
end

Class-level lock (same concept)

Team.obtain_lock(:reorder, team_id) do
Team.reorder_all_players(team_id)
end

== You Likely Have Some Huge Bugs
== Brush Up Your Resume

You are probably not handling atomic operations properly in your app, and
probably have some nasty lurking race conditions. The worst part is these
Expand Down Expand Up @@ -243,4 +151,4 @@ phone call from your boss. (At least, not about this...)
== Author

Copyright (c) 2009 {Nate Wiger}[http://nate.wiger.org]. All Rights Reserved.
Released under the {Artistic License}[http://www.opensource.org/licenses/artistic-license-2.0.php].
Rant released under {Creative Commons}[http://creativecommons.org/licenses/by/3.0/legalcode].
50 changes: 29 additions & 21 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
= Redis Objects - Lightweight object layer around redis-rb you can also use in a model
= Redis Objects - Lightweight, atomic object layer around redis-rb

Redis is great _not_ as a replacement for MySQL, but as a way to perform atomic
operations on _individual_ data structures, like counters, lists, and sets. People
Expand All @@ -17,16 +17,16 @@ by using +new+ with the type of data structure you want to create.
gem tumble
gem install redis-objects

== Initialization
=== Initialization

# If on Rails, config/initializers/redis.rb is a good place for this
require 'redis'
require 'redis/objects'
Redis::Objects.redis = Redis.new(:host => 127.0.0.1, :port => 6379)

== Examples of Class Usage
== Examples

Model class:
=== Model Class Usage

class Team < ActiveRecord::Base
include Redis::Objects
Expand All @@ -35,11 +35,34 @@ Model class:
counter :active_players
counter :total_online_players, :global => true
list :player_stats
set :
set :outfielders
lock :reorder, :timeout => 5 # seconds
end

Familiar Ruby operations Just Work (TM):

Using counters to handle concurrency:
=== Instance Usage

=== Counters

@counter = Redis::Counter.new('counter_name')
@counter.increment
puts @counter
puts @counter.get # force re-fetch

=== Lists

@list = Redis::List.new('list_name')
@list << 'a'
@list << 'b'
puts @list

== Atomicity

You are probably not handling atomicity correctly in your app. For a fun rant
on the topic, see {ATOMICITY}[ATOMICITY.doc]

Atomic counters are a good way to handle concurrency:

@team = Team.find(1)
if @team.drafted_players.increment <= @team.max_players
Expand Down Expand Up @@ -94,21 +117,6 @@ Class-level lock (same concept)
Team.reorder_all_players(team_id)
end

== Examples of Instance Usage

=== Counters

@counter = Redis::Counter.new('counter_name')
@counter.increment
puts @counter
puts @counter.get # force re-fetch

=== Lists

@list = Redis::List.new('list_name')
@list << 'a'
@list << 'b'
puts @list

== Author

Expand Down

0 comments on commit 8dfb1c1

Please sign in to comment.