|
1 |
| -= Redis Objects - Lightweight object layer around redis-rb you can also use in a model |
| 1 | += An Atomic Rant |
2 | 2 |
|
3 |
| -Redis is great _not_ as a replacement for MySQL, but as a way to perform atomic |
4 |
| -operations on _individual_ data structures, like counters, lists, and sets. People |
5 |
| -that are wrapping ORM's around Redis are missing the point. |
6 |
| - |
7 |
| -This gem, instead, provides atomic methods that you can use *with* your existing |
8 |
| -ActiveRecord/DataMapper/etc models, or in classes that have nothing to do with an |
9 |
| -ORM or even a database, but need support for high-performance atomic operations. |
10 |
| - |
11 |
| -The only requirement Redis::Atoms has is that your class must provide an +id+ instance |
12 |
| -method which returns the ID for that instance. ActiveRecord, DataMapper, and MongoRecord |
13 |
| -all have id methods which are known to be suitable. Since +id+ can be anything as |
14 |
| -far as Redis::Atoms is concerned, you can even write an +id+ method of your own that |
15 |
| -just returns a string, or an MD5 of the name, or something else unique. |
16 |
| - |
17 |
| -== Installation |
18 |
| - |
19 |
| - gem install gemcutter |
20 |
| - gem tumble |
21 |
| - gem install redis-atoms |
22 |
| - |
23 |
| -== Example |
24 |
| - |
25 |
| -Somewhere in your app initialization |
26 |
| - |
27 |
| - require 'redis' |
28 |
| - require 'redis/atoms' |
29 |
| - Redis::Atoms.redis = Redis.new(:host => 127.0.0.1, :port => 6379) |
30 |
| - |
31 |
| -Model class: |
32 |
| - |
33 |
| - class Team < ActiveRecord::Base |
34 |
| - include Redis::Atoms |
35 |
| - |
36 |
| - counter :drafted_players |
37 |
| - counter :active_players |
38 |
| - lock :reorder, :timeout => 5 # seconds |
39 |
| - end |
40 |
| - |
41 |
| -Using counters to handle concurrency: |
42 |
| - |
43 |
| - @team = Team.find(1) |
44 |
| - if @team.drafted_players.increment <= @team.max_players |
45 |
| - # do stuff |
46 |
| - @team.team_players.create!(:player_id => 221) |
47 |
| - @team.active_players.increment |
48 |
| - else |
49 |
| - # reset counter state |
50 |
| - @team.drafted_players.decrement |
51 |
| - end |
52 |
| - |
53 |
| -Atomic block - a cleaner way to do the above. Exceptions or nil results |
54 |
| -rewind counter back to previous state: |
55 |
| - |
56 |
| - @team.drafted_players.increment do |val| |
57 |
| - raise Team::TeamFullError if val > @team.max_players |
58 |
| - @team.team_players.create!(:player_id => 221) |
59 |
| - @team.active_players.increment |
60 |
| - end |
61 |
| - |
62 |
| -Similar approach, using an if block (failure rewinds counter): |
63 |
| - |
64 |
| - @team.drafted_players.increment do |val| |
65 |
| - if val <= @team.max_players |
66 |
| - @team.team_players.create!(:player_id => 221) |
67 |
| - @team.active_players.increment |
68 |
| - end |
69 |
| - end |
70 |
| - |
71 |
| -Class methods work too - notice we override ActiveRecord counters: |
72 |
| - |
73 |
| - Team.increment_counter :drafted_players, team_id |
74 |
| - Team.decrement_counter :drafted_players, team_id, 2 |
75 |
| - |
76 |
| -Class-level atomic block (may save a DB fetch depending on your app): |
77 |
| - |
78 |
| - Team.increment_counter(:drafted_players, team_id) do |val| |
79 |
| - TeamPitcher.create!(:team_id => team_id, :pitcher_id => 181) |
80 |
| - Team.increment_counter(:active_players, team_id) |
81 |
| - end |
82 |
| - |
83 |
| -Locks with Redis. On completion or exception the lock is released: |
84 |
| - |
85 |
| - @team.reorder_lock.lock do |
86 |
| - @team.reorder_all_players |
87 |
| - end |
88 |
| - |
89 |
| -Class-level lock (same concept) |
90 |
| - |
91 |
| - Team.obtain_lock(:reorder, team_id) do |
92 |
| - Team.reorder_all_players(team_id) |
93 |
| - end |
94 |
| - |
95 |
| -== You Likely Have Some Huge Bugs |
| 3 | +== Brush Up Your Resume |
96 | 4 |
|
97 | 5 | You are probably not handling atomic operations properly in your app, and
|
98 | 6 | probably have some nasty lurking race conditions. The worst part is these
|
@@ -243,4 +151,4 @@ phone call from your boss. (At least, not about this...)
|
243 | 151 | == Author
|
244 | 152 |
|
245 | 153 | Copyright (c) 2009 {Nate Wiger}[http://nate.wiger.org]. All Rights Reserved.
|
246 |
| -Released under the {Artistic License}[http://www.opensource.org/licenses/artistic-license-2.0.php]. |
| 154 | +Rant released under {Creative Commons}[http://creativecommons.org/licenses/by/3.0/legalcode]. |
0 commit comments