Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 142 lines (125 sloc) 5.095 kB
a472223 @skiz Updated specs and tested complete
authored
1 class Mongo::Queue
2e7ed43 @skiz working:
authored
2 attr_reader :connection, :config
3
4 DEFAULT_CONFIG = {
5 :database => 'mongo_queue',
6 :collection => 'mongo_queue',
a472223 @skiz Updated specs and tested complete
authored
7 :timeout => 300,
2e7ed43 @skiz working:
authored
8 :attempts => 3
9 }.freeze
10
11 DEFAULT_INSERT = {
12 :priority => 0,
13 :attempts => 0,
14 :locked_by => nil,
15 :locked_at => nil,
16 :last_error => nil
17 }.freeze
18
a472223 @skiz Updated specs and tested complete
authored
19 # Create a new instance of MongoQueue with the provided mongodb connection and optional configuration.
20 # See +DEFAULT_CONFIG+ for default configuration and possible configuration options.
21 #
22 # Example:
23 # db = Mongo::Connection.new('localhost')
24 # config = {:timeout => 90, :attempts => 2}
25 # queue = Mongo::Queue.new(db, config)
26 #
2e7ed43 @skiz working:
authored
27 def initialize(connection, opts={})
28 @connection = connection
29 @config = DEFAULT_CONFIG.merge(opts)
30 end
31
a472223 @skiz Updated specs and tested complete
authored
32 # Remove all items from the queue. Use with caution!
33 def flush!
34 collection.drop
35 end
36
2e7ed43 @skiz working:
authored
37 # Insert a new item in to the queue with required queue message parameters.
a472223 @skiz Updated specs and tested complete
authored
38 #
39 # Example:
40 # queue.insert(:name => 'Billy', :email => 'billy@example.com', :message => 'Here is the thing you asked for')
2e7ed43 @skiz working:
authored
41 def insert(hash)
a472223 @skiz Updated specs and tested complete
authored
42 id = collection.insert DEFAULT_INSERT.merge(hash)
43 collection.find_one(:_id => Mongo::ObjectID.from_string(id.to_s))
2e7ed43 @skiz working:
authored
44 end
45
a472223 @skiz Updated specs and tested complete
authored
46 # Lock and return the next queue message if one is available. Returns nil if none are available. Be sure to
47 # review the README.rdoc regarding proper usage of the locking process identifier (locked_by).
48 # Example:
49 # locked_doc = queue.lock_next(Thread.current.object_id)
50 def lock_next(locked_by)
51 cmd = OrderedHash.new
52 cmd['findandmodify'] = @config[:collection]
53 cmd['update'] = {'$set' => {:locked_by => locked_by, :locked_at => Time.now.utc}}
54 cmd['query'] = {:locked_by => nil, :locked_by => nil, :attempts => {'$lt' => @config[:attempts]}}
55 cmd['sort'] = sort_hash
56 cmd['limit'] = 1
57 cmd['new'] = true
58 value_of collection.db.command(cmd)
2e7ed43 @skiz working:
authored
59 end
60
a472223 @skiz Updated specs and tested complete
authored
61 # Removes stale locks that have exceeded the timeout and places them back in the queue.
62 def cleanup!
63 cursor = collection.find({:locked_by => /.*/, :locked_at => {'$lt' => Time.now.utc - config[:timeout]}})
2e7ed43 @skiz working:
authored
64 doc = cursor.next_document
65 while doc
66 release(doc, doc['locked_by'])
67 doc = cursor.next_document
68 end
69 end
a472223 @skiz Updated specs and tested complete
authored
70
71 # Release a lock on the specified document and allow it to become available again.
72 def release(doc, locked_by)
73 cmd = OrderedHash.new
74 cmd['findandmodify'] = @config[:collection]
75 cmd['update'] = {'$set' => {:locked_by => nil, :locked_at => nil}}
76 cmd['query'] = {:locked_by => locked_by, :_id => Mongo::ObjectID.from_string(doc['_id'].to_s)}
77 cmd['limit'] = 1
78 cmd['new'] = true
79 value_of collection.db.command(cmd)
80 end
81
82 # Remove the document from the queue. This should be called when the work is done and the document is no longer needed.
83 # You must provide the process identifier that the document was locked with to complete it.
84 def complete(doc, locked_by)
85 cmd = OrderedHash.new
86 cmd['findandmodify'] = @config[:collection]
87 cmd['query'] = {:locked_by => locked_by, :_id => Mongo::ObjectID.from_string(doc['_id'].to_s)}
88 cmd['remove'] = true
89 cmd['limit'] = 1
90 value_of collection.db.command(cmd)
91 end
92
93 # Increase the error count on the locked document and release. Optionally provide an error message.
94 def error(doc, error_message=nil)
95 doc['attempts'] +=1
96 collection.save doc.merge({
97 'last_error' => error_message,
98 'locked_by' => nil,
99 'locked_at' => nil
100 })
b52b0d3 added support for queue stats. locked/available/errors
Josh Martin authored
101 end
102
a2baac6 Version bump to 0.2.0
Josh Martin authored
103 # Provides some information about what is in the queue. We are using an eval to ensure that a
104 # lock is obtained during the execution of this query so that the results are not skewed.
105 # please be aware that it will lock the database during the execution, so avoid using it too
106 # often, even though it it very tiny and should be relatively fast.
b52b0d3 added support for queue stats. locked/available/errors
Josh Martin authored
107 def stats
a2baac6 Version bump to 0.2.0
Josh Martin authored
108 js = "function queue_stat(){
109 return db.eval(
110 function(){
111 var a = db.#{config[:collection]}.count({'locked_by': null, 'attempts': {$lt: #{config[:attempts]}}});
112 var l = db.#{config[:collection]}.count({'locked_by': /.*/});
113 var e = db.#{config[:collection]}.count({'attempts': {$gte: #{config[:attempts]}}});
114 var t = db.#{config[:collection]}.count();
115 return [a, l, e, t];
116 }
117 );
118 }"
119 available, locked, errors, total = collection.db.eval(js)
120 { :locked => locked.to_i,
121 :errors => errors.to_i,
122 :available => available.to_i,
123 :total => total.to_i }
b52b0d3 added support for queue stats. locked/available/errors
Josh Martin authored
124 end
125
2e7ed43 @skiz working:
authored
126
127 protected
128
a472223 @skiz Updated specs and tested complete
authored
129 def sort_hash #:nodoc:
130 sh = OrderedHash.new
131 sh['priority'] = -1 ; sh
132 end
133
134 def value_of(result) #:nodoc:
135 result['okay'] == 0 ? nil : result['value']
136 end
137
138 def collection #:nodoc:
2e7ed43 @skiz working:
authored
139 @connection.db(@config[:database]).collection(@config[:collection])
140 end
141 end
Something went wrong with that request. Please try again.