2012-08-23 13:03:50 -04:00
require 'securerandom'
2012-02-08 20:04:02 -05:00
require 'sidekiq/middleware/chain'
2012-01-21 19:42:21 -05:00
module Sidekiq
class Client
2012-02-10 23:20:01 -05:00
2012-02-19 16:02:32 -05:00
def self . default_middleware
Middleware :: Chain . new do | m |
end
end
2012-02-15 14:28:19 -05:00
def self . registered_workers
2012-03-14 12:56:13 -04:00
Sidekiq . redis { | x | x . smembers ( 'workers' ) }
2012-02-15 14:28:19 -05:00
end
def self . registered_queues
2012-03-14 12:56:13 -04:00
Sidekiq . redis { | x | x . smembers ( 'queues' ) }
2012-02-15 14:28:19 -05:00
end
2012-04-19 17:42:17 -04:00
##
# The main method used to push a job to Redis. Accepts a number of options:
#
# queue - the named queue to use, default 'default'
# class - the worker class to call, required
# args - an array of simple arguments to the perform method, must be JSON-serializable
# retry - whether to retry this job if it fails, true or false, default true
2012-04-27 23:25:46 -04:00
# backtrace - whether to save any error backtrace, default false
2012-04-19 17:42:17 -04:00
#
# All options must be strings, not symbols. NB: because we are serializing to JSON, all
# symbols in 'args' will be converted to strings.
#
2012-08-11 14:47:25 -04:00
# Returns nil if not pushed to Redis or a unique Job ID if pushed.
#
2012-04-19 17:42:17 -04:00
# Example:
# Sidekiq::Client.push('queue' => 'my_queue', 'class' => MyWorker, 'args' => ['foo', 1, :bat => 'bar'])
#
2012-04-01 22:53:45 -04:00
def self . push ( item )
2012-09-11 23:53:22 -04:00
normed = normalize_item ( item )
normed , payload = process_single ( item [ 'class' ] , normed )
2012-01-21 19:42:21 -05:00
2012-09-11 23:53:22 -04:00
pushed = false
2012-10-25 23:55:32 -04:00
pushed = raw_push ( normed , payload ) if normed
2012-09-11 23:53:22 -04:00
pushed ? normed [ 'jid' ] : nil
end
2012-04-26 11:40:07 -04:00
2012-09-11 23:53:22 -04:00
##
# Push a large number of jobs to Redis. In practice this method is only
# useful if you are pushing tens of thousands of jobs or more. This method
# basically cuts down on the redis round trip latency.
#
# Takes the same arguments as Client.push except that args is expected to be
# an Array of Arrays. All other keys are duplicated for each job. Each job
# is run through the client middleware pipeline and each job gets its own Job ID
# as normal.
#
# Returns the number of jobs pushed or nil if the pushed failed. The number of jobs
# pushed can be less than the number given if the middleware stopped processing for one
# or more jobs.
2012-09-13 20:38:56 -04:00
def self . push_bulk ( items )
2012-09-11 23:53:22 -04:00
normed = normalize_item ( items )
payloads = items [ 'args' ] . map do | args |
_ , payload = process_single ( items [ 'class' ] , normed . merge ( 'args' = > args , 'jid' = > SecureRandom . hex ( 12 ) ) )
payload
end . compact
2012-02-10 23:20:01 -05:00
pushed = false
2012-11-19 20:34:46 -05:00
pushed = raw_push ( normed , payloads )
2012-09-11 23:53:22 -04:00
pushed ? payloads . size : nil
2012-01-21 19:42:21 -05:00
end
2012-09-07 11:35:27 -04:00
# Resque compatibility helpers.
2012-01-21 19:42:21 -05:00
#
2012-09-07 11:35:27 -04:00
# Example usage:
# Sidekiq::Client.enqueue(MyWorker, 'foo', 1, :bat => 'bar')
2012-01-21 19:42:21 -05:00
#
2012-02-09 23:32:59 -05:00
# Messages are enqueued to the 'default' queue.
2012-01-21 19:42:21 -05:00
#
def self . enqueue ( klass , * args )
2012-09-07 11:59:04 -04:00
klass . client_push ( 'class' = > klass , 'args' = > args )
2012-01-21 19:42:21 -05:00
end
2012-07-17 10:48:54 -04:00
2012-09-07 11:35:27 -04:00
# Example usage:
# Sidekiq::Client.enqueue_to(:queue_name, MyWorker, 'foo', 1, :bat => 'bar')
2012-07-17 10:48:54 -04:00
#
def self . enqueue_to ( queue , klass , * args )
2012-09-07 11:59:04 -04:00
klass . client_push ( 'queue' = > queue , 'class' = > klass , 'args' = > args )
2012-07-17 10:48:54 -04:00
end
2012-09-11 23:53:22 -04:00
private
2012-10-25 23:55:32 -04:00
def self . raw_push ( normed , payload ) # :nodoc:
pushed = false
Sidekiq . redis do | conn |
2012-11-20 00:31:14 -05:00
if normed [ 'at' ] && payload . is_a? ( Array )
pushed = conn . zadd ( 'schedule' , payload . map { | hash | [ normed [ 'at' ] . to_s , hash ] } )
elsif normed [ 'at' ]
2012-10-25 23:55:32 -04:00
pushed = conn . zadd ( 'schedule' , normed [ 'at' ] . to_s , payload )
else
_ , pushed = conn . multi do
conn . sadd ( 'queues' , normed [ 'queue' ] )
conn . rpush ( " queue: #{ normed [ 'queue' ] } " , payload )
end
end
end
pushed
end
2012-09-11 23:53:22 -04:00
def self . process_single ( worker_class , item )
queue = item [ 'queue' ]
Sidekiq . client_middleware . invoke ( worker_class , item , queue ) do
payload = Sidekiq . dump_json ( item )
return item , payload
end
end
def self . normalize_item ( item )
raise ( ArgumentError , " Message must be a Hash of the form: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] } " ) unless item . is_a? ( Hash )
raise ( ArgumentError , " Message must include a class and set of arguments: #{ item . inspect } " ) if ! item [ 'class' ] || ! item [ 'args' ]
2012-11-15 15:58:58 -05:00
raise ( ArgumentError , " Message class must be either a Class or String representation of the class name " ) unless item [ 'class' ] . is_a? ( Class ) || item [ 'class' ] . is_a? ( String )
if item [ 'class' ] . is_a? ( Class )
2012-11-15 23:34:33 -05:00
raise ( ArgumentError , " Message must include a Sidekiq::Worker class, not class name: #{ item [ 'class' ] . ancestors . inspect } " ) if ! item [ 'class' ] . respond_to? ( 'get_sidekiq_options' )
normalized_item = item [ 'class' ] . get_sidekiq_options . merge ( item )
2012-11-15 15:58:58 -05:00
normalized_item [ 'class' ] = normalized_item [ 'class' ] . to_s
else
2012-11-15 23:34:33 -05:00
normalized_item = Sidekiq :: Worker :: ClassMethods :: DEFAULT_OPTIONS . merge ( item )
2012-11-15 15:58:58 -05:00
end
2012-09-11 23:53:22 -04:00
normalized_item [ 'jid' ] = SecureRandom . hex ( 12 )
2012-09-17 23:07:30 -04:00
normalized_item
2012-09-11 23:53:22 -04:00
end
2012-01-21 19:42:21 -05:00
end
end