Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throttling jobs via arbitrary resource - Issue #27 #183

Closed
wants to merge 103 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
8a9839d
Add API for handling throttling
Mar 11, 2014
51fbae6
initial lua code for job concurrency throttling
wr0ngway Mar 11, 2014
647c5b8
pass throttle when enqueing a job
wr0ngway Mar 11, 2014
a48aa5c
fix typo
wr0ngway Mar 11, 2014
d6b5fe8
Update qless lua scripts to enable concurrency
Mar 11, 2014
70b1712
Only put throttle if a throttle exists
Mar 11, 2014
f762a2e
Update qless lua scripts for multiple throttles
Mar 12, 2014
3e41d42
Add throttles accessor to job
Mar 12, 2014
e297e55
Update lua scripts with queue throttling count
Mar 12, 2014
ab69d8a
Fix throttle integration
Mar 12, 2014
a982b6d
test fixes
Mar 12, 2014
69f79e6
new qless core scripts to fix throttling deadlock
Mar 13, 2014
5648149
updated qless scripts with no printlines
Mar 13, 2014
db57a0c
Add API to set throttle expiration and retrieve throttle TTL
Mar 13, 2014
b74a033
Merge pull request #1 from backupify/throttling
Mar 13, 2014
d167817
Update lua scripts
Mar 13, 2014
37ea6cc
expose throttles throughout UI
wr0ngway Mar 14, 2014
00fcec6
fix test till cancel is fixed in qless
wr0ngway Mar 14, 2014
2e47132
Merge pull request #2 from backupify/complete_throttle_ui
wr0ngway Mar 14, 2014
b22a90d
updated qless core lua scripts
Apr 2, 2014
85a49c0
Merge pull request #3 from backupify/throttling-refactor
Apr 11, 2014
ce13508
update throttle access for Qless::Queue
Apr 17, 2014
5cbc066
Merge pull request #4 from backupify/update_throttle_controls
Apr 17, 2014
c96a70a
basic throttle management ui
Apr 22, 2014
f539337
DRY test
Apr 23, 2014
9cdbc2c
additional comments
Apr 23, 2014
0216155
update throttle erb
Apr 23, 2014
73df52d
remove comment
Apr 23, 2014
7037cdb
update throttle endpoints
Apr 25, 2014
4d5a659
add pry to gemspec
Apr 25, 2014
7fb8028
can set expiration of throttles from ui
Apr 25, 2014
da391b4
change 'Delete' to 'Reset'
Apr 25, 2014
ebca222
Merge pull request #5 from backupify/throttle_ui
Apr 25, 2014
f2578bc
add ui for job throttles
Apr 29, 2014
ddc9752
fix spacing
Apr 30, 2014
6c439c9
Merge pull request #6 from backupify/job_throttle_ui
Apr 30, 2014
89de250
updated qless lua scripts
May 12, 2014
e5a9865
fix queue throttle ui
May 12, 2014
6408e5c
use throttle directly from queue for testing
May 13, 2014
6f40628
return throttle from queue for client_throttle#counts
May 13, 2014
b7ac72b
replace ':'s with '-'s for throttle classes
May 14, 2014
30e1c16
Merge pull request #7 from backupify/queue_throttle_ui_fix
May 14, 2014
51f3a50
prevent minimum version check
May 17, 2014
4ba82cb
test for skippin redis check
May 19, 2014
54cf22c
peer review suggestions
May 19, 2014
c0d8c1d
peer review
May 19, 2014
6f7bcc9
Merge pull request #8 from backupify/lazily-redis-connect
May 19, 2014
c8e1c42
ruby-version
Jun 3, 2014
abe5b04
updated qless core
Jun 3, 2014
14ac2a5
Update .ruby-version
Jun 3, 2014
d54e0a6
Merge pull request #10 from backupify/update-qless-core
Jun 3, 2014
c12b768
updated qless-core
Jun 4, 2014
688ecce
Merge pull request #11 from backupify/update-core
Jun 4, 2014
4c56ea0
adds a processing change to the procline
Jun 13, 2014
ceb1838
Merge pull request #12 from backupify/processing-procline
Jun 13, 2014
2bb4a5e
fix error in qless
Aug 7, 2014
9cbc49a
fix for workers becoming stuck
Aug 7, 2014
0fa4402
merging upstream
Aug 8, 2014
09a5ee3
fix test
Aug 8, 2014
627a36d
fix requeue from losing throttles
May 4, 2015
edce4d7
update qless-core
May 4, 2015
f4ca002
Merge pull request #14 from backupify/update-core
james-lawrence May 4, 2015
cdda62c
merge upstream
May 4, 2015
bfab8a4
update job view
May 4, 2015
aefdd3f
Merge pull request #15 from backupify/update-master
james-lawrence May 5, 2015
dd87a47
Fix brittle test.
myronmarston Mar 19, 2014
0c68724
Use something more intention revealing.
myronmarston Mar 19, 2014
3ca9a66
Improve RetryExceptions middleware to support exception-dependent bac…
myronmarston Apr 17, 2014
ec963e3
Rename `move` to `dequeue`.
myronmarston Jun 12, 2014
4c95952
Use new `requeue` qless-core command.
myronmarston Jun 12, 2014
1a63c47
add after_retry callback handler for RetryExceptions middleware
Jun 18, 2014
b49e454
add an after_requeue callback to RequeueExceptions
Jun 18, 2014
0f7370b
check block.nil? instead of block_given?
Jun 18, 2014
e0d9cdc
only allow one callback and provide explicit method for setting callback
Jun 19, 2014
094bbc1
get rid of unnecessary test config code
Jun 19, 2014
eb0b5a7
Fix typo.
myronmarston Jul 30, 2014
18ad696
fix requeue from losing throttles
May 4, 2015
0fe9ee6
fix reeqeueue
May 6, 2015
d9712a9
Merge branch 'fix-requeue' of github.com:backupify/qless into fix-req…
May 6, 2015
484c387
undo bad merge
May 6, 2015
41859e7
fix bad merge
May 6, 2015
02ff8a4
fix bad merge
May 6, 2015
3fc6de1
wip
May 6, 2015
4a013c2
test fixes, cleanup
May 8, 2015
b1eed41
strong test
May 11, 2015
7e6592e
peer review
May 12, 2015
9deb039
Merge pull request #16 from backupify/fix-requeue
james-lawrence May 12, 2015
7239d21
wip
May 20, 2015
d3d1ad2
Merge branch 'master' into reduce-subscriptions
May 20, 2015
0e20d3b
wip
May 20, 2015
55e770e
wip
May 20, 2015
2dbbb49
test fix
May 20, 2015
9f64571
combine if statements
May 20, 2015
6ae4868
Merge pull request #17 from backupify/reduce-subscriptions
james-lawrence May 20, 2015
70c68e8
update from qless-core popop_retry branch - retry pop up to config li…
wr0ngway Jul 23, 2015
d13d389
dont inherit when looking up constants
Feb 20, 2015
e2f964b
Merge pull request #13 from backupify/use_constantize
jbodah Aug 10, 2015
5425a0f
update core
Sep 10, 2015
df8a785
Use the backupify version of qless-core
Jul 5, 2016
77585b1
Update qless-core
Jul 5, 2016
58a8b90
Update core to fix moved job throttles
Jul 8, 2016
91965f3
No longer prints stack trace in qless error storage
mwooddatto Oct 31, 2017
cb1eed2
Merge pull request #19 from backupify/BFY-3692-qless-stack-trace-no
mwooddatto Nov 1, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
*.gem
*~
.DS_STORE
.bundle
pkg/*
.DS_STORE

spec/redis.config.yml
bundle
spec/tmp
coverage
bin

phantomjs/

.rspec-local
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "lib/qless/qless-core"]
path = lib/qless/qless-core
url = https://github.com/seomoz/qless-core.git
url = https://github.com/backupify/qless-core.git
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this line should not be pushed upstream.

8 changes: 8 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ group :extras do
end

gem 'thin' # needed by qless-web binary

group :development do
gem 'byebug', :platforms => [:ruby_20, :ruby_21]
gem 'pry'
gem 'pry-byebug', :platforms => [:ruby_20, :ruby_21]
gem 'pry-stack_explorer'
gem 'cane', :platforms => [:ruby_20, :ruby_21]
end
26 changes: 26 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ GEM
ast (1.1.0)
atomic (1.1.14)
avl_tree (1.1.3)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
byebug (2.7.0)
columnize (~> 0.3)
debugger-linecache (~> 1.2)
cane (2.6.2)
parallel
capybara (1.1.4)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
Expand All @@ -20,8 +27,10 @@ GEM
xpath (~> 0.1.4)
childprocess (0.3.9)
ffi (~> 1.0, >= 1.0.11)
coderay (1.1.0)
columnize (0.3.6)
daemons (1.1.9)
debug_inspector (0.0.2)
debugger (1.6.2)
columnize (>= 0.3.1)
debugger-linecache (~> 1.2.0)
Expand All @@ -40,6 +49,7 @@ GEM
http_parser.rb (0.5.3)
launchy (2.1.2)
addressable (~> 2.3)
method_source (0.8.2)
metriks (0.9.9.5)
atomic (~> 1.0)
avl_tree (~> 1.1.2)
Expand All @@ -50,6 +60,7 @@ GEM
multipart-post (1.2.0)
nokogiri (1.6.0)
mini_portile (~> 0.5.0)
parallel (1.4.1)
parser (2.0.0.pre8)
ast (~> 1.1)
slop (~> 3.4, >= 3.4.5)
Expand All @@ -60,6 +71,16 @@ GEM
http_parser.rb (~> 0.5.3)
multi_json (~> 1.0)
powerpack (0.0.8)
pry (0.10.1)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
pry-byebug (1.3.3)
byebug (~> 2.7)
pry (~> 0.10)
pry-stack_explorer (0.4.9.2)
binding_of_caller (>= 0.7)
pry (>= 0.9.11)
rack (1.5.2)
rack-protection (1.5.0)
rack
Expand Down Expand Up @@ -119,12 +140,17 @@ PLATFORMS
ruby

DEPENDENCIES
byebug
cane
capybara (~> 1.1.2)
debugger
faye-websocket (~> 0.4.0)
launchy (~> 2.1.0)
metriks (~> 0.9)
poltergeist (~> 1.0.0)
pry
pry-byebug
pry-stack_explorer
qless!
rake (~> 10.0)
rspec (~> 2.12)
Expand Down
52 changes: 31 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
qless
qless [![Build Status](https://travis-ci.org/seomoz/qless.svg?branch=master)](https://travis-ci.org/seomoz/qless)
=====

Qless is a powerful `Redis`-based job queueing system inspired by
Expand Down Expand Up @@ -162,31 +162,41 @@ it is empty, before trying to pop job off the second queue. The
round-robin reserver will pop a job off the first queue, then the second
queue, and so on. You could also easily implement your own.

To start a worker, load the qless rake tasks in your Rakefile, and
define a `qless:setup` task:
To start a worker, write a bit of Ruby code that instantiates a
worker and runs it. You could write a rake task to do this, for
example:

``` ruby
require 'qless/tasks'
namespace :qless do
task :setup do
require 'my_app/environment' # to ensure all job classes are loaded

# Set options via environment variables
# The only required option is QUEUES; the
# rest have reasonable defaults.
ENV['REDIS_URL'] ||= 'redis://some-host:7000/3'
ENV['QUEUES'] ||= 'fizz,buzz'
ENV['JOB_RESERVER'] ||= 'Ordered'
ENV['INTERVAL'] ||= '10' # 10 seconds
ENV['VERBOSE'] ||= 'true'
end
end
```
desc "Run a Qless worker"
task :work do
# Load your application code. All job classes must be loaded.
require 'my_app/environment'

Then run the `qless:work` rake task:
# Require the parts of qless you need
require 'qless'
require 'qless/job_reservers/ordered'
require 'qless/worker'

```
rake qless:work
# Create a client
client = Qless::Client.new(:host => 'foo.bar.com', :port => 1234)

# Get the queues you use
queues = %w[ queue_1 queue_2 ].map do |name|
client.queues[name]
end

# Create a job reserver; different reservers use different
# strategies for which order jobs are popped off of queues
reserver = Qless::JobReservers::Ordered.new(queues)

# Create a forking worker that uses the given reserver to pop jobs.
worker = Qless::Workers::ForkingWorker.new(reserver)

# Start the worker!
worker.run
end
end
```

The following signals are supported in the parent process:
Expand Down
64 changes: 61 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ task :check_coverage do
end
end

task default: [:spec, :check_coverage]
task default: [:spec, :check_coverage, :cane]

namespace :core do
qless_core_dir = "./lib/qless/qless-core"
Expand Down Expand Up @@ -82,15 +82,24 @@ namespace :core do
task verify: %w[ verify:clean verify:current ]
end

desc "Starts a qless console"
task :console do
ENV['PUBLIC_SEQUEL_API'] = 'true'
ENV['NO_NEW_RELIC'] = 'true'
exec "bundle exec pry -r./conf/console"
end

require 'qless/tasks'

namespace :qless do
task :setup do
desc "Runs a test worker so you can send signals to it for testing"
task :run_test_worker do
require 'qless'
require 'qless/job_reservers/ordered'
require 'qless/worker'
queue = Qless::Client.new.queues["example"]
queue.client.redis.flushdb

ENV['QUEUES'] = queue.name
ENV['VVERBOSE'] = '1'

class ExampleJob
Expand All @@ -105,6 +114,55 @@ namespace :qless do
20.times do |i|
queue.put(ExampleJob, sleep: i)
end

reserver = Qless::JobReservers::Ordered.new([queue])
Qless::Workers::ForkingWorker.new(reserver, log_level: Logger::INFO).run
end
end


namespace :cane do
begin
require 'cane/rake_task'

libs = [
{ name: 'qless', dir: '.', root: '.' },
]

libs.each do |lib|
desc "Runs cane code quality checks for #{lib[:name]}"
Cane::RakeTask.new(lib[:name]) do |cane|
cane.no_doc = true

cane.abc_glob = "#{lib[:dir]}/{lib,spec}/**/*.rb"
cane.abc_max = 15
cane.abc_exclude = %w[
Middleware::(anon)#expect_job_to_timeout
Qless::Job#initialize
Qless::Middleware::RequeueExceptions#handle_exception
Qless::Middleware::Timeout#initialize
Qless::WorkerHelpers#run_jobs
Qless::Workers::BaseWorker#initialize
Qless::Workers::BaseWorker#register_signal_handlers
Qless::Workers::ForkingWorker#register_signal_handlers
Qless::Workers::SerialWorker#run
]

cane.style_glob = "#{lib[:dir]}/lib/**/*.rb"
cane.style_measure = 100
cane.style_exclude = %w[
]
end
end

desc "Runs cane code quality checks for all projects"
task all: libs.map { |l| l[:name] }

rescue LoadError
task :all do
puts "cane is not supported in ruby #{RUBY_VERSION}"
end
end
end

task cane: "cane:all"
20 changes: 20 additions & 0 deletions conf/console.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)

require 'irb/completion'

QLESS_CONSOLE = true

require 'qless'

module StdoutLogger
def logger
@logger ||= Logger.new($stdout)
end
end

# Load everything!
Dir["./lib/**/*.rb"].sort.each do |f|
require f.gsub("./lib/", "")
end

require 'pp'
2 changes: 1 addition & 1 deletion exe/install_phantomjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ then
brew install phantomjs
elif [[ "$os_name" == 'Linux' ]]
then
version=phantomjs-1.7.0-linux-i686
version=phantomjs-1.7.0-linux-x86_64
wget http://phantomjs.googlecode.com/files/$version.tar.bz2
tar xjf $version.tar.bz2
mv $version phantomjs
Expand Down
2 changes: 1 addition & 1 deletion exe/qless-growl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require 'ruby-growl'
require 'micro-optparse'

@options = Parser.new do |p|
p.banner = 'This agent lets you get campfire notifications for the progress of tracked jobs'
p.banner = 'This agent lets you get growl notifications for the progress of tracked jobs'
p.option :growl , 'host for the growl daemon', :default => 'localhost'
p.option :app , 'application name for notifications', :default => 'qless'
p.option :host , 'host:port for your qless redis instance', :default => 'localhost:6379'
Expand Down
27 changes: 25 additions & 2 deletions lib/qless.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module Qless
require 'qless/version'
require 'qless/config'
require 'qless/queue'
require 'qless/throttle'
require 'qless/job'
require 'qless/lua_script'
require 'qless/failure_formatter'
Expand Down Expand Up @@ -135,6 +136,24 @@ def [](name)
end
end

# A class for interacting with throttles. Not meant to be instantiated directly,
# it's accessed through Client#throttles
class ClientThrottles
def initialize(client)
@client = client
end

def [](name)
Throttle.new(name, @client)
end

def counts
@client.queues.counts.map do |queue|
Queue.new(queue['name'], @client).throttle
end
end
end

# A class for interacting with events. Not meant to be instantiated directly,
# it's accessed through Client#events
class ClientEvents
Expand Down Expand Up @@ -169,20 +188,24 @@ def stop
# The client for interacting with Qless
class Client
# Lua script
attr_reader :_qless, :config, :redis, :jobs, :queues, :workers
attr_reader :_qless, :config, :redis, :jobs, :queues, :throttles, :workers
attr_accessor :worker_name

def initialize(options = {})
default_options = {:ensure_minimum_version => true}
options = default_options.merge(options)

# This is the redis instance we're connected to. Use connect so REDIS_URL
# will be honored
@redis = options[:redis] || Redis.connect(options)
@options = options
assert_minimum_redis_version('2.5.5')
assert_minimum_redis_version('2.5.5') if @options.delete(:ensure_minimum_version)
@config = Config.new(self)
@_qless = Qless::LuaScript.new('qless', @redis)

@jobs = ClientJobs.new(self)
@queues = ClientQueues.new(self)
@throttles = ClientThrottles.new(self)
@workers = ClientWorkers.new(self)
@worker_name = [Socket.gethostname, Process.pid.to_s].join('-')
end
Expand Down
Loading