monque 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +3 -0
- data/lib/monque.rb +155 -0
- data/lib/monque/tasks.rb +12 -0
- metadata +69 -0
data/Rakefile
ADDED
data/lib/monque.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mongo'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Kernel
|
6
|
+
# from http://redcorundum.blogspot.com/2006/05/kernelqualifiedconstget.html
|
7
|
+
def fetch_class(str)
|
8
|
+
path = str.to_s.split('::')
|
9
|
+
from_root = path[0].empty?
|
10
|
+
if from_root
|
11
|
+
from_root = []
|
12
|
+
path = path[1..-1]
|
13
|
+
else
|
14
|
+
start_ns = ((Class === self)||(Module === self)) ? self : self.class
|
15
|
+
from_root = start_ns.to_s.split('::')
|
16
|
+
end
|
17
|
+
until from_root.empty?
|
18
|
+
begin
|
19
|
+
return (from_root+path).inject(Object) { |ns,name| ns.const_get(name) }
|
20
|
+
rescue NameError
|
21
|
+
from_root.delete_at(-1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
path.inject(Object) { |ns,name| ns.const_get(name) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module Monque
|
29
|
+
RETRY_DELAY = 1800 # wait this number secs before trying a job again
|
30
|
+
REPLICATION_FACTOR = 1
|
31
|
+
|
32
|
+
@mongo_host = '127.0.0.1'
|
33
|
+
@mongo_port = 27017
|
34
|
+
|
35
|
+
def self.mongo_host=(h); @mongo_host = h; end
|
36
|
+
def self.mongo_port=(p); @mongo_port = p; end
|
37
|
+
|
38
|
+
def self.jobs_collection(host=nil, port=nil)
|
39
|
+
host ||= @mongo_host
|
40
|
+
port ||= @mongo_port
|
41
|
+
|
42
|
+
Mongo::Connection.new(host, port)['monque']['jobs']
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.queue_for_cls(cls)
|
46
|
+
if cls.kind_of?(Class) || cls.kind_of?(Module)
|
47
|
+
if qname = cls.instance_variable_get(:@queue)
|
48
|
+
qname.is_a?(Symbol) ? qname.to_s : qname
|
49
|
+
else
|
50
|
+
raise "You should define the @queue name for #{cls.inspect}"
|
51
|
+
end
|
52
|
+
else
|
53
|
+
raise TypeError.new("The first argument to enqueue should be a class")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.enqueue(cls, *args)
|
58
|
+
queue = queue_for_cls(cls)
|
59
|
+
raise NameError.new("Invalid queue name: #{queue.inspect}") unless queue.kind_of?(String)
|
60
|
+
|
61
|
+
@jobs ||= jobs_collection
|
62
|
+
@jobs.save({
|
63
|
+
'queue' => queue,
|
64
|
+
'class' => cls.name,
|
65
|
+
'data' => args.to_json,
|
66
|
+
'started' => 0,
|
67
|
+
'created' => Time.now.to_f,
|
68
|
+
'proc_attempts' => []},
|
69
|
+
{:safe => {:w => REPLICATION_FACTOR}}
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
class Worker
|
74
|
+
def initialize(host, port, queues)
|
75
|
+
@queues = queues
|
76
|
+
@jobs = Monque.jobs_collection(host, port)
|
77
|
+
end
|
78
|
+
|
79
|
+
def worker_id
|
80
|
+
@worker_id ||= "#{`hostname`.strip}-#{$$}-#{Time.now.to_f}"
|
81
|
+
end
|
82
|
+
|
83
|
+
def reserve
|
84
|
+
@queues.each do |q|
|
85
|
+
speculative_job = @jobs.find(
|
86
|
+
'queue' => q.to_s,
|
87
|
+
'started' => {'$lte' => (Time.now.to_f - RETRY_DELAY)},
|
88
|
+
'finished' => {'$exists' => false}
|
89
|
+
).sort('added', :ascending).limit(1).first
|
90
|
+
|
91
|
+
next unless speculative_job
|
92
|
+
|
93
|
+
old_procid = speculative_job['procid']
|
94
|
+
|
95
|
+
gotted_job = @jobs.find_and_modify(
|
96
|
+
:query => {
|
97
|
+
'_id' => speculative_job['_id'],
|
98
|
+
'proc_attempts' => speculative_job['proc_attempts']
|
99
|
+
},
|
100
|
+
:update => {
|
101
|
+
'$set' => {'started' => Time.now.to_f},
|
102
|
+
'$push' => {
|
103
|
+
'proc_attempts' => {
|
104
|
+
'id' => sprintf("%x", rand(1024**3)),
|
105
|
+
'by' => worker_id,
|
106
|
+
'time' => Time.now.to_f
|
107
|
+
}
|
108
|
+
}
|
109
|
+
},
|
110
|
+
:new => true
|
111
|
+
)
|
112
|
+
|
113
|
+
if gotted_job
|
114
|
+
return gotted_job
|
115
|
+
else
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def mark_finished(job)
|
124
|
+
ok = @jobs.find_and_modify(
|
125
|
+
:query => {
|
126
|
+
'_id' => job['_id'],
|
127
|
+
'proc_attempts' => job['proc_attempts']
|
128
|
+
},
|
129
|
+
:update => job.merge({'finished' => Time.now.to_f})
|
130
|
+
)
|
131
|
+
rescue Mongo::OperationFailure => e
|
132
|
+
if e.message =~ /No matching object found/
|
133
|
+
raise "This shouldn't happen - job was marked as finished though I had it reserved."
|
134
|
+
else
|
135
|
+
raise e
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def work
|
140
|
+
loop do
|
141
|
+
job = reserve
|
142
|
+
|
143
|
+
if job
|
144
|
+
puts "job: #{job.inspect}"
|
145
|
+
cls = Kernel.fetch_class(job['class'])
|
146
|
+
$stderr.puts "#{worker_id}: processing #{job.inspect}"
|
147
|
+
cls.send(:perform, *JSON.load(job['data']))
|
148
|
+
mark_finished(job)
|
149
|
+
end
|
150
|
+
|
151
|
+
sleep 5
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/monque/tasks.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
namespace :monque do
|
2
|
+
task :work do
|
3
|
+
raise "Must specify QUEUES env var" if ENV['QUEUES'].nil?
|
4
|
+
raise "Must specify mongo host:port" if ENV['MONGO'].nil?
|
5
|
+
|
6
|
+
queues = ENV['QUEUES'].split(',')
|
7
|
+
mongo = ENV['MONGO'].split(':')
|
8
|
+
|
9
|
+
worker = Monque::Worker.new(mongo[0], mongo[1], queues)
|
10
|
+
worker.work
|
11
|
+
end
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: monque
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Patrick Collison
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-09-03 00:00:00 +00:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Simple queue on top of MongoDB, conforming roughly to Resque's API
|
23
|
+
email: patrick@collison.ie
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/monque/tasks.rb
|
32
|
+
- lib/monque.rb
|
33
|
+
- Rakefile
|
34
|
+
has_rdoc: true
|
35
|
+
homepage: http://collison.ie/monque
|
36
|
+
licenses: []
|
37
|
+
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
hash: 3
|
49
|
+
segments:
|
50
|
+
- 0
|
51
|
+
version: "0"
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project: monque
|
64
|
+
rubygems_version: 1.3.7
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Pre-release beta version of Monque
|
68
|
+
test_files: []
|
69
|
+
|