kthxbye 1.0.0
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/.document +5 -0
- data/.gitignore +33 -0
- data/DESIGN.textile +81 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +42 -0
- data/LICENSE +20 -0
- data/README.textile +91 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/config.ru +7 -0
- data/lib/kthxbye.rb +151 -0
- data/lib/kthxbye/config.rb +35 -0
- data/lib/kthxbye/exceptions.rb +4 -0
- data/lib/kthxbye/failure.rb +62 -0
- data/lib/kthxbye/helper.rb +42 -0
- data/lib/kthxbye/job.rb +127 -0
- data/lib/kthxbye/version.rb +5 -0
- data/lib/kthxbye/web_interface.rb +117 -0
- data/lib/kthxbye/web_interface/public/application.js +16 -0
- data/lib/kthxbye/web_interface/public/awesome-buttons.css +108 -0
- data/lib/kthxbye/web_interface/public/jquery.js +154 -0
- data/lib/kthxbye/web_interface/public/style.css +128 -0
- data/lib/kthxbye/web_interface/views/error.haml +5 -0
- data/lib/kthxbye/web_interface/views/failed.haml +26 -0
- data/lib/kthxbye/web_interface/views/hash.haml +6 -0
- data/lib/kthxbye/web_interface/views/layout.haml +33 -0
- data/lib/kthxbye/web_interface/views/overview.haml +2 -0
- data/lib/kthxbye/web_interface/views/queues.haml +31 -0
- data/lib/kthxbye/web_interface/views/set.haml +4 -0
- data/lib/kthxbye/web_interface/views/stats.haml +32 -0
- data/lib/kthxbye/web_interface/views/view_backtrace.haml +8 -0
- data/lib/kthxbye/web_interface/views/workers.haml +24 -0
- data/lib/kthxbye/web_interface/views/working.haml +19 -0
- data/lib/kthxbye/worker.rb +221 -0
- data/test/helper.rb +18 -0
- data/test/redis-test.conf +115 -0
- data/test/test_failure.rb +51 -0
- data/test/test_helper.rb +86 -0
- data/test/test_kthxbye.rb +213 -0
- data/test/test_worker.rb +148 -0
- metadata +364 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
|
4
|
+
# rdoc generated
|
5
|
+
rdoc
|
6
|
+
|
7
|
+
# yard generated
|
8
|
+
doc
|
9
|
+
.yardoc
|
10
|
+
|
11
|
+
# bundler
|
12
|
+
.bundle
|
13
|
+
|
14
|
+
# jeweler generated
|
15
|
+
pkg
|
16
|
+
## MAC OS
|
17
|
+
.DS_Store
|
18
|
+
|
19
|
+
## TEXTMATE
|
20
|
+
*.tmproj
|
21
|
+
tmtags
|
22
|
+
|
23
|
+
## EMACS
|
24
|
+
*~
|
25
|
+
\#*
|
26
|
+
.\#*
|
27
|
+
|
28
|
+
## VIM
|
29
|
+
*.swp
|
30
|
+
*.swo
|
31
|
+
|
32
|
+
## PROJECT::SPECIFIC
|
33
|
+
*.gemspec
|
data/DESIGN.textile
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
h1. Queues
|
2
|
+
|
3
|
+
There are a number of important queues that we employ to track data:
|
4
|
+
|
5
|
+
# queues - responsilbe for pushing/popping jobs. only stores a unique id. Is
|
6
|
+
pushed and popped, therefore a fairly "volatile" dataset.
|
7
|
+
# data-store:[name] - hash responsible for the different types of job data.
|
8
|
+
Stored by job id. This data is persisted until a job is successful or failed
|
9
|
+
too many times. Then the job will be dropped and failure recorded.
|
10
|
+
# result-store:[name] - hash responsible for storing the results by job id.
|
11
|
+
Data will remain here until removed. Can be fetched multiple times if desired
|
12
|
+
providing a cached retrieval.
|
13
|
+
# failure-store:[name] - hash responsible for storing any failed jobs. Stored
|
14
|
+
until manually cleared.
|
15
|
+
|
16
|
+
In addition there is a unique id counter that gets incremented as the job queue
|
17
|
+
grows. It is stored at "unique_id"
|
18
|
+
|
19
|
+
On top of these queues, there is also a set of stores setup to keep track of
|
20
|
+
workers and processes actively being worked.
|
21
|
+
|
22
|
+
# workers - set that tracks all the registered workers. All workers not in this list
|
23
|
+
should be destroyed.
|
24
|
+
# working - set of workers that are actively working a job.
|
25
|
+
|
26
|
+
h1. Jobs
|
27
|
+
|
28
|
+
I decided to make jobs a two fold purpose vehicle. One, it would be the job
|
29
|
+
storage mechanism through which a job would be placed on the queue for later
|
30
|
+
execution by workers and two, it would actually do the job execution, even
|
31
|
+
though the worker is the one that calls it. This way all job related tasks
|
32
|
+
can be abstracted to the job class, while the worker can busy itself about
|
33
|
+
handling the control of the job execution and not the job execution itself.
|
34
|
+
|
35
|
+
Jobs will also be responsible for storing the state of the result and any
|
36
|
+
failures that come from the job execution.
|
37
|
+
|
38
|
+
A job can only live on one queue.
|
39
|
+
|
40
|
+
Job will try 3 times before being taken out of the work queue and placed in the
|
41
|
+
failure queue
|
42
|
+
|
43
|
+
h1. Workers
|
44
|
+
|
45
|
+
There was a big struggle in the beginning to decide how to work queued jobs.
|
46
|
+
I originally wanted to do a more cooperative scheduling schema but found that
|
47
|
+
would not end up taking multiple cores into consideration. Moreover, it is
|
48
|
+
wicked hard to preempt fibers in a meaningful way without digging really deep
|
49
|
+
into other code as well in order to retrofit them for Fibers or EventMachine.
|
50
|
+
So I simply didn't.
|
51
|
+
|
52
|
+
Instead I went with Ruby's Process library and decided to work at making it
|
53
|
+
work as much as possible around a Unix-style processing queue so that in one
|
54
|
+
fell swoop, we could use multiple cores as well as gain true concurrency.
|
55
|
+
One more design decision is that we will not attempt to run multiple processes-
|
56
|
+
per-worker. This way we can control the number of processes running by how many
|
57
|
+
workers we choose to run, rather than by how many processes a worker is allowed
|
58
|
+
to spawn.
|
59
|
+
|
60
|
+
This presents a slight problem however. Each worker then will involve two
|
61
|
+
processes. One parent/control-loop and one child/job-processor. This means in
|
62
|
+
the end, we have more processes running at once, possibly chewing up more
|
63
|
+
resources than might be ultimately necessary. This will hopefully be overcome
|
64
|
+
by suspending the parent worker while the child process runs. This way one core
|
65
|
+
is not chewed up by an essentially idle process. Hopefully this can be benchmarked
|
66
|
+
to figure out if its faster to run one-to-many parent/childs or one-to-one.
|
67
|
+
|
68
|
+
A worker can work multiple queues
|
69
|
+
|
70
|
+
h1. Web Interface
|
71
|
+
|
72
|
+
Intended to have a web frontend to control/observe workers, jobs and queues.
|
73
|
+
|
74
|
+
h1. Job Observer Widget
|
75
|
+
|
76
|
+
Intended to have a JS widget that will indicate when a job has been computed
|
77
|
+
and the results are ready or a job has failed. Potential application for a
|
78
|
+
Node.js implementation (using something like
|
79
|
+
http://github.com/fictorial/redis-node-client). Should be easily embeddable in
|
80
|
+
any page for simple notification.
|
81
|
+
|
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "shoulda", ">= 0"
|
10
|
+
gem "bundler", "~> 1.0.0"
|
11
|
+
gem "jeweler", "~> 1.5.0.pre3"
|
12
|
+
gem "rcov", ">= 0"
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
gem "redis"
|
17
|
+
gem "yajl-ruby"
|
18
|
+
gem "json"
|
19
|
+
gem "mail"
|
20
|
+
gem 'i18n'
|
21
|
+
gem 'sinatra'
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.0.0)
|
5
|
+
git (1.2.5)
|
6
|
+
i18n (0.4.1)
|
7
|
+
jeweler (1.5.0.pre3)
|
8
|
+
bundler (~> 1.0.0)
|
9
|
+
git (>= 1.2.5)
|
10
|
+
rake
|
11
|
+
json (1.4.6)
|
12
|
+
mail (2.2.6.1)
|
13
|
+
activesupport (>= 2.3.6)
|
14
|
+
mime-types
|
15
|
+
treetop (>= 1.4.5)
|
16
|
+
mime-types (1.16)
|
17
|
+
polyglot (0.3.1)
|
18
|
+
rack (1.2.1)
|
19
|
+
rake (0.8.7)
|
20
|
+
rcov (0.9.9)
|
21
|
+
redis (2.0.10)
|
22
|
+
shoulda (2.11.3)
|
23
|
+
sinatra (1.0)
|
24
|
+
rack (>= 1.0)
|
25
|
+
treetop (1.4.8)
|
26
|
+
polyglot (>= 0.3.1)
|
27
|
+
yajl-ruby (0.7.7)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
bundler (~> 1.0.0)
|
34
|
+
i18n
|
35
|
+
jeweler (~> 1.5.0.pre3)
|
36
|
+
json
|
37
|
+
mail
|
38
|
+
rcov
|
39
|
+
redis
|
40
|
+
shoulda
|
41
|
+
sinatra
|
42
|
+
yajl-ruby
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Luke van der Hoeven
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
h1. kthxbye
|
2
|
+
|
3
|
+
Kthxbye is the answer to a fairly unique-yet-common problem: Background job
|
4
|
+
processing when we care about the result.
|
5
|
+
|
6
|
+
There are a number of projects I can think of where a job that takes longer than
|
7
|
+
a user ought to be waiting for a result (due to server timeout length or out of
|
8
|
+
simple app responsiveness courtesy to the user) and yet I need the result of the
|
9
|
+
operation to be returned to the user.
|
10
|
+
|
11
|
+
Here's a real-world example. I work with a set of legacy Oracle databases that
|
12
|
+
stores much of our business logic as PLSQL procedures. Yes, this is not "The
|
13
|
+
Rails Way™" but it's the only way for the company I work for right now.
|
14
|
+
Many of the procedures that I run as part of several of the applications I
|
15
|
+
support can take on average one minute or more with a standard deviation of
|
16
|
+
almost 2 minutes (with a forced timeout of 5 minutes). That's kinda a long time
|
17
|
+
to sit and wait on a web app.
|
18
|
+
|
19
|
+
<img src="http://img.skitch.com/20100901-gadna641fj4wdeswgj74y2pssq.png" alt="RLA - IWP Analysis"/>
|
20
|
+
|
21
|
+
We don't really want users sitting waiting for up to 5 minutes (when it forces
|
22
|
+
failure) unable to do anything or (even worse) hitting refresh or the action
|
23
|
+
again. Especially bad when this can mean the HTTP server is getting backed up
|
24
|
+
as more and more people run these long running processes.
|
25
|
+
|
26
|
+
Moreover, the users need to get response from the completed job before moving
|
27
|
+
on. Most job processors (DJ, Resque) are setup for running jobs that do not
|
28
|
+
require the result to be returned to the web app (think mass-mailers, queue
|
29
|
+
population, image resizing). They just run and the output goes to a database,
|
30
|
+
an inbox or a file server.
|
31
|
+
|
32
|
+
h2. Enter Kthxbye
|
33
|
+
|
34
|
+
Kthxbye is an attempt to solve this problem. It is based heavily off of
|
35
|
+
"Resque":http://github.com/defunkt/resque and why not an addition to Resque?
|
36
|
+
I needed some hack time with Redis on my own as I've never used it before...
|
37
|
+
|
38
|
+
bq. I can learn any language or tool in a matter of days if you give me
|
39
|
+
1. a good manual
|
40
|
+
2. an even better project to work on.
|
41
|
+
|
42
|
+
_- Prof. Shumacher_
|
43
|
+
|
44
|
+
This project accomplishes both those goals. This is an attempt to learn
|
45
|
+
something, using Resque and Redis docs as a manual, while at the same time
|
46
|
+
creating a much needed solution to a problem.
|
47
|
+
|
48
|
+
The idea is to be able to do the following:
|
49
|
+
|
50
|
+
# dummy job class
|
51
|
+
class MyJob
|
52
|
+
def self.perform(data)
|
53
|
+
puts "Do something with #{data}"
|
54
|
+
data.gsub(/hello/i, "Goodbye")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# setup options, then connect
|
59
|
+
Kthxbye::Config.setup(:redis_server => 'localhost', :redis_port => 8080)
|
60
|
+
|
61
|
+
# each enqueued job returns a unique id to poll with
|
62
|
+
unique_id = Kthxbye.enqueue("jobs", MyJob, "Hello World")
|
63
|
+
|
64
|
+
# ... code code code ...
|
65
|
+
|
66
|
+
# polls queue every 5 seconds
|
67
|
+
computed_value = Kthxbye.poll("jobs", unique_id, 5)
|
68
|
+
|
69
|
+
and then in some other world, on some other machine, (that still has knowledge of MyJob)
|
70
|
+
|
71
|
+
# inits with queue
|
72
|
+
worker = Kthxbye::Worker.new("jobs")
|
73
|
+
|
74
|
+
# connects to queue and runs jobs found there
|
75
|
+
worker.run
|
76
|
+
|
77
|
+
Pretty... damn... simple.
|
78
|
+
|
79
|
+
h2. Note on Patches/Pull Requests
|
80
|
+
|
81
|
+
* Fork the project.
|
82
|
+
* Make your feature addition or bug fix.
|
83
|
+
* Add tests for it. This is important so I don't break it in a
|
84
|
+
future version unintentionally.
|
85
|
+
* Commit, do not mess with rakefile, version, or history.
|
86
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
87
|
+
* Send me a pull request. Bonus points for topic branches.
|
88
|
+
|
89
|
+
h2. Copyright
|
90
|
+
|
91
|
+
Copyright (c) 2010 Luke van der Hoeven. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
gem.name = "kthxbye"
|
15
|
+
gem.summary = %Q{Async processing + results notification}
|
16
|
+
gem.description = %Q{Kthxbye is the answer to a fairly unique-yet-common problem: Background job processing when we care about the result.}
|
17
|
+
gem.email = "hungerandthirst@gmail.com"
|
18
|
+
gem.homepage = "http://github.com/plukevdh/kthxbye"
|
19
|
+
gem.authors = ["Luke van der Hoeven"]
|
20
|
+
gem.add_development_dependency "shoulda", ">= 0"
|
21
|
+
gem.add_development_dependency "bundler", "~> 1.0.0"
|
22
|
+
gem.add_development_dependency "jeweler", "~> 1.5.0.pre3"
|
23
|
+
gem.add_development_dependency "rcov", ">= 0"
|
24
|
+
gem.add_dependency "redis", "~> 2.0.5"
|
25
|
+
gem.add_dependency "yajl-ruby", ">= 0.7.7"
|
26
|
+
gem.add_dependency "json", "~> 1.4.6"
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
require 'rake/testtask'
|
30
|
+
Rake::TestTask.new(:test) do |test|
|
31
|
+
test.libs << 'lib' << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'rcov/rcovtask'
|
37
|
+
Rcov::RcovTask.new do |test|
|
38
|
+
test.libs << 'test'
|
39
|
+
test.pattern = 'test/**/test_*.rb'
|
40
|
+
test.verbose = true
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "kthxbye #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/config.ru
ADDED
data/lib/kthxbye.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'redis'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'yajl'
|
5
|
+
rescue
|
6
|
+
require 'json'
|
7
|
+
end
|
8
|
+
|
9
|
+
$LOAD_PATH << './lib'
|
10
|
+
|
11
|
+
require 'kthxbye/config'
|
12
|
+
require 'kthxbye/helper'
|
13
|
+
require 'kthxbye/job'
|
14
|
+
require 'kthxbye/worker'
|
15
|
+
require 'kthxbye/failure'
|
16
|
+
|
17
|
+
require 'kthxbye/version'
|
18
|
+
|
19
|
+
require 'kthxbye/exceptions'
|
20
|
+
|
21
|
+
module Kthxbye
|
22
|
+
include Helper
|
23
|
+
extend self
|
24
|
+
|
25
|
+
#takes in an existing redis instance or simply connects a new instance
|
26
|
+
def connect( redis_instance=nil )
|
27
|
+
@redis = ( redis_instance || Redis.new( :host => Config.options[:redis_server], :port => Config.options[:redis_port] ) )
|
28
|
+
end
|
29
|
+
|
30
|
+
def redis
|
31
|
+
return @redis if @redis
|
32
|
+
Config.setup
|
33
|
+
self.connect
|
34
|
+
self.redis
|
35
|
+
end
|
36
|
+
|
37
|
+
def keys
|
38
|
+
redis.keys("*")
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
def enqueue(queue, klass, *args)
|
43
|
+
Job.create(queue, klass, *args)
|
44
|
+
end
|
45
|
+
|
46
|
+
# gets the size of a given queue
|
47
|
+
def size(queue)
|
48
|
+
redis.llen("queue:#{queue}").to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
# gets the latest latest job off the given queue
|
52
|
+
# returns a Job object
|
53
|
+
def salvage(q)
|
54
|
+
id = redis.lpop( "queue:#{q}" )
|
55
|
+
if id
|
56
|
+
payload = decode( redis.hget( "data-store:#{q}", id ) )
|
57
|
+
return Job.new(id, q, payload)
|
58
|
+
else
|
59
|
+
log "No jobs found in #{q}"
|
60
|
+
return nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# lets us peek at the data to be run with a job
|
65
|
+
# can lookup an entire queue or for a specific job id
|
66
|
+
def peek(store, queue, id=nil)
|
67
|
+
if id
|
68
|
+
decode( redis.hget( "#{store}-store:#{queue}", id ) )
|
69
|
+
else
|
70
|
+
all = redis.hgetall( "#{store}-store:#{queue}" )
|
71
|
+
results = {}
|
72
|
+
all.each {|k,v| results[k] = decode( v ) }
|
73
|
+
return results
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# handles a few of our dynamic methods
|
78
|
+
def method_missing(name, *args)
|
79
|
+
method_name = name.id2name
|
80
|
+
if method_name =~ /^(data|result)_peek$/
|
81
|
+
Kthxbye.send(:peek, $1, *args)
|
82
|
+
else
|
83
|
+
super
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
# returns all the queues Kthxbye knows about
|
89
|
+
def queues
|
90
|
+
redis.smembers( :queues ).sort
|
91
|
+
end
|
92
|
+
|
93
|
+
# registers the queue in our "known queues" list
|
94
|
+
def register_queue(queue)
|
95
|
+
redis.sadd(:queues, queue) unless redis.sismember(:queues, queue)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Removes the queue from the active queue listing, does not delete queue
|
99
|
+
# will lead to phantom queues. use delete_queue for complete removal of queue
|
100
|
+
def unregister_queue(queue)
|
101
|
+
redis.srem(:queues, queue)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Completely removes queue: unregisters it then deletes it
|
105
|
+
# should return true in all cases
|
106
|
+
def delete_queue(queue)
|
107
|
+
unregister_queue(queue)
|
108
|
+
redis.del( "queue:#{queue}" ) || true
|
109
|
+
end
|
110
|
+
|
111
|
+
# returns all our registered workers
|
112
|
+
def workers
|
113
|
+
workers = redis.smembers( :workers )
|
114
|
+
workers.map {|x| Worker.find( x ) }
|
115
|
+
end
|
116
|
+
|
117
|
+
# returns all our active workers and the job they are working
|
118
|
+
def working
|
119
|
+
workers = redis.smembers( :working )
|
120
|
+
data = []
|
121
|
+
workers.each do |w_id|
|
122
|
+
data << [w_id, decode( redis.get("worker:#{w_id}") )]
|
123
|
+
end
|
124
|
+
return data
|
125
|
+
end
|
126
|
+
|
127
|
+
# returns either the job results for a specific job (if id specified)
|
128
|
+
# or all the results for all the jobs on a queue
|
129
|
+
def job_results(queue, id=nil)
|
130
|
+
if id
|
131
|
+
decode( redis.hget( "result-store:#{queue}", id ) )
|
132
|
+
else
|
133
|
+
Array( redis.hgetall( "result-store:#{queue}" ) )
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def inspect
|
138
|
+
{
|
139
|
+
:version => Version,
|
140
|
+
:keys => keys.size,
|
141
|
+
:workers => workers.size,
|
142
|
+
:working => working.size,
|
143
|
+
:queues => queues.size,
|
144
|
+
:failed => Failure.count,
|
145
|
+
:pending => queues.inject(0) {|m,o| m + size(o)}
|
146
|
+
}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
|