sidekiq-dynamic-queues 0.5.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/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ *.DS_Store
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
6
+ .idea
7
+ spec/dump.rdb
8
+ spec/redis-server.log
9
+ spec/redis.pid
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - jruby-19mode # JRuby in 1.9 mode
5
+ # - rbx-19mode
6
+ script: bundle exec rspec spec
data/CHANGELOG ADDED
@@ -0,0 +1,5 @@
1
+ 0.5.0
2
+ -----
3
+
4
+ Initial version ported from resque-dynamic-queues 0.8.1
5
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sidekiq-dynamic-queues.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+
2
+ The MIT License (MIT)
3
+ Copyright (c) 2013 Matt Conway (matt@conwaysplace.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ A sidekiq plugin for specifying the queues a worker pulls from with wildcards, negations, or dynamic look up from redis.
2
+
3
+ Authored against Sidekiq 2.9.0, so it at least works with that - try running the tests if you use a different version of sidekiq
4
+
5
+ [![Build Status](https://secure.travis-ci.org/wr0ngway/sidekiq-dynamic-queues.png)](http://travis-ci.org/wr0ngway/sidekiq-dynamic-queues)
6
+
7
+ Usage:
8
+
9
+ If creating a gem of your own that uses sidekiq-dynamic-queues, you may have to add an explicit require statement at the top of your Rakefile:
10
+
11
+ require 'sidekiq-dynamic-queues'
12
+
13
+ Configure by setting Sidekiq.options[:fetch] = Sidekiq::DynamicQueues::Fetch
14
+
15
+ Start your workers with a QUEUE that can contain '\*' for zero-or more of any character, '!' to exclude the following pattern, or @key to look up the patterns from redis. Some examples help:
16
+
17
+ sidekiq -q foo
18
+
19
+ Pulls jobs from the queue 'foo'
20
+
21
+ sidekiq -q *
22
+
23
+ Pulls jobs from any queue
24
+
25
+ sidekiq -q *foo
26
+
27
+ Pulls jobs from queues that end in foo
28
+
29
+ sidekiq -q *foo*
30
+
31
+ Pulls jobs from queues whose names contain foo
32
+
33
+ sidekiq -q *foo* -q !foobar
34
+
35
+ Pulls jobs from queues whose names contain foo except the foobar queue
36
+
37
+ sidekiq -q *foo* -q !*bar
38
+
39
+ Pulls jobs from queues whose names contain foo except queues whose names end in bar
40
+
41
+ QUEUE='@key' sidekiq -q @key
42
+
43
+ Pulls jobs from queue names stored in redis (use Sidekiq::DynamicQueues::Attributes.set\_dynamic\_queue("key", ["queuename1", "queuename2"]) to set them)
44
+
45
+ sidekiq -q * -q !@key
46
+
47
+ Pulls jobs from any queue except ones stored in redis
48
+
49
+ sidekiq -q @
50
+
51
+ Pulls jobs from queue names stored in redis using the hostname of the worker
52
+
53
+ Sidekiq::DynamicQueues::Attributes.set_dynamic_queue("key", ["*foo*", "!*bar"])
54
+ sidekiq -q @key
55
+
56
+ Pulls jobs from queue names stored in redis, with wildcards/negations
57
+
58
+
59
+ There is also a tab in the sidekiq-web UI that allows you to define the dynamic queues To activate it, you need to require 'sidekiq-dynamic-queues-server' in whatever initializer you use to bring up sidekiq-web.
60
+
61
+
62
+ Contributors:
63
+
64
+ Matt Conway ( https://github.com/wr0ngway )
data/Rakefile ADDED
@@ -0,0 +1,67 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ task :my_release => ['changelog', 'release'] do
5
+ end
6
+
7
+ task :changelog do
8
+
9
+ helper = Bundler::GemHelper.new(Dir.pwd)
10
+ version = "v#{helper.gemspec.version}"
11
+
12
+ changelog_file = 'CHANGELOG'
13
+ entries = ""
14
+
15
+ # Get a list of current tags
16
+ tags = `git tag -l`.split
17
+ tags = tags.sort_by {|t| t[1..-1].split(".").collect {|s| s.to_i } }
18
+ newest_tag = tags[-1]
19
+
20
+ if version == newest_tag
21
+ puts "You need to update version, same as most recent tag: #{version}"
22
+ exit
23
+ end
24
+
25
+ # If we already have a changelog, make the last tag be the
26
+ # last one in the changelog, and the next one be the one
27
+ # following that in the tag list
28
+ newest_changelog_version = nil
29
+ if File.exist?(changelog_file)
30
+ entries = File.read(changelog_file)
31
+ head = entries.split.first
32
+ if head =~ /\d\.\d\.\d/
33
+ newest_changelog_version = "v#{head}"
34
+
35
+ if version == newest_changelog_version
36
+ puts "You need to update version, same as most recent changelog: #{version}"
37
+ exit
38
+ end
39
+
40
+ end
41
+ end
42
+
43
+ # Generate changelog from repo
44
+ log=`git log --pretty='format:%s <%h> [%cn]' #{newest_tag}..HEAD`
45
+
46
+ # Strip out maintenance entries
47
+ log = log.lines.to_a.delete_if do |l|
48
+ l =~ /^Regenerated? gemspec/ ||
49
+ l =~ /^version bump/i ||
50
+ l =~ /^Updated changelog/ ||
51
+ l =~ /^Merged? branch/
52
+ end
53
+
54
+ # Write out changelog file
55
+ File.open(changelog_file, 'w') do |out|
56
+ out.puts version.gsub(/^v/, '')
57
+ out.puts "-----"
58
+ out.puts "\n"
59
+ out.puts log
60
+ out.puts "\n"
61
+ out.puts entries
62
+ end
63
+
64
+ # Commit and push
65
+ sh "git ci -m'Updated changelog' #{changelog_file}"
66
+ sh "git push"
67
+ end
data/config.ru ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'logger'
3
+
4
+ $LOAD_PATH.unshift ::File.expand_path(::File.dirname(__FILE__) + '/lib')
5
+ require 'sidekiq/web'
6
+ require 'sidekiq-dynamic-queues-server'
7
+
8
+ use Rack::ShowExceptions
9
+ run Sidekiq::Web
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'sidekiq-dynamic-queues'
@@ -0,0 +1,5 @@
1
+ require 'sidekiq-dynamic-queues'
2
+ require 'sidekiq/web'
3
+ require 'sidekiq/dynamic_queues/server'
4
+
5
+ Sidekiq::Web.register Sidekiq::DynamicQueues::Server
@@ -0,0 +1,3 @@
1
+ require 'sidekiq'
2
+ require 'sidekiq/dynamic_queues/attributes'
3
+ require 'sidekiq/dynamic_queues/fetch'
@@ -0,0 +1,65 @@
1
+ module Sidekiq
2
+ module DynamicQueues
3
+
4
+ DYNAMIC_QUEUE_KEY = "dynamic_queue"
5
+ FALLBACK_KEY = "default"
6
+
7
+ module Attributes
8
+ extend self
9
+
10
+ def json_encode(data)
11
+ Sidekiq.dump_json(data)
12
+ end
13
+
14
+ def json_decode(data)
15
+ return nil unless data
16
+ Sidekiq.load_json(data)
17
+ end
18
+
19
+ def get_dynamic_queue(key, fallback=['*'])
20
+ data = Sidekiq.redis {|r| r.hget(DYNAMIC_QUEUE_KEY, key) }
21
+ queue_names = json_decode(data)
22
+
23
+ if queue_names.nil? || queue_names.size == 0
24
+ data = Sidekiq.redis {|r| r.hget(DYNAMIC_QUEUE_KEY, FALLBACK_KEY) }
25
+ queue_names = json_decode(data)
26
+ end
27
+
28
+ if queue_names.nil? || queue_names.size == 0
29
+ queue_names = fallback
30
+ end
31
+
32
+ return queue_names
33
+ end
34
+
35
+ def set_dynamic_queue(key, values)
36
+ if values.nil? or values.size == 0
37
+ Sidekiq.redis {|r| r.hdel(DYNAMIC_QUEUE_KEY, key) }
38
+ else
39
+ Sidekiq.redis {|r| r.hset(DYNAMIC_QUEUE_KEY, key, json_encode(values)) }
40
+ end
41
+ end
42
+
43
+ def set_dynamic_queues(dynamic_queues)
44
+ Sidekiq.redis do |r|
45
+ r.multi do
46
+ r.del(DYNAMIC_QUEUE_KEY)
47
+ dynamic_queues.each do |k, v|
48
+ set_dynamic_queue(k, v)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def get_dynamic_queues
55
+ result = {}
56
+ queues = Sidekiq.redis {|r| r.hgetall(DYNAMIC_QUEUE_KEY) }
57
+ queues.each {|k, v| result[k] = json_decode(v) }
58
+ result[FALLBACK_KEY] ||= ['*']
59
+ return result
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,84 @@
1
+ require 'sidekiq/fetch'
2
+
3
+ module Sidekiq
4
+ module DynamicQueues
5
+
6
+ # enable with Sidekiq.options[:fetch] = Sidekiq::DynamicQueues::Fetch
7
+ class Fetch < Sidekiq::BasicFetch
8
+
9
+ include Sidekiq::Util
10
+ include Sidekiq::DynamicQueues::Attributes
11
+
12
+ def initialize(options)
13
+ super
14
+ @dynamic_queues = options[:queues]
15
+ end
16
+
17
+ # overriding Sidekiq::BasicFetch#queues_cmd
18
+ def queues_cmd
19
+ if @dynamic_queues.grep(/(^!)|(^@)|(\*)/).size == 0
20
+ super
21
+ else
22
+ queues = expanded_queues
23
+ queues = @strictly_ordered_queues ? queues : queues.shuffle
24
+ queues << Sidekiq::Fetcher::TIMEOUT
25
+ end
26
+ end
27
+
28
+ # Returns a list of queues to use when searching for a job.
29
+ #
30
+ # A splat ("*") means you want every queue (in alpha order) - this
31
+ # can be useful for dynamically adding new queues.
32
+ #
33
+ # The splat can also be used as a wildcard within a queue name,
34
+ # e.g. "*high*", and negation can be indicated with a prefix of "!"
35
+ #
36
+ # An @key can be used to dynamically look up the queue list for key from redis.
37
+ # If no key is supplied, it defaults to the worker's hostname, and wildcards
38
+ # and negations can be used inside this dynamic queue list. Set the queue
39
+ # list for a key with
40
+ # Sidekiq::DynamicQueues::Attributes.set_dynamic_queue(key, ["q1", "q2"]
41
+ #
42
+ def expanded_queues
43
+ queue_names = @dynamic_queues.dup
44
+
45
+ real_queues = Sidekiq::Client.registered_queues
46
+ matched_queues = []
47
+
48
+ while q = queue_names.shift
49
+ q = q.to_s
50
+
51
+ if q =~ /^(!)?@(.*)/
52
+ key = $2.strip
53
+ key = hostname if key.size == 0
54
+
55
+ add_queues = get_dynamic_queue(key)
56
+ add_queues.map! { |q| q.gsub!(/^!/, '') || q.gsub!(/^/, '!') } if $1
57
+
58
+ queue_names.concat(add_queues)
59
+ next
60
+ end
61
+
62
+ if q =~ /^!/
63
+ negated = true
64
+ q = q[1..-1]
65
+ end
66
+
67
+ patstr = q.gsub(/\*/, ".*")
68
+ pattern = /^#{patstr}$/
69
+ if negated
70
+ matched_queues -= matched_queues.grep(pattern)
71
+ else
72
+ matches = real_queues.grep(/^#{pattern}$/)
73
+ matches = [q] if matches.size == 0 && q == patstr
74
+ matched_queues.concat(matches)
75
+ end
76
+ end
77
+
78
+ return matched_queues.collect { |q| "queue:#{q}" }.uniq.sort
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,69 @@
1
+ require 'sidekiq-dynamic-queues'
2
+
3
+ module Sidekiq
4
+ module DynamicQueues
5
+ module Server
6
+
7
+ Attr = Sidekiq::DynamicQueues::Attributes
8
+
9
+ def self.registered(app)
10
+
11
+ app.helpers do
12
+
13
+ def find_template(view,*a,&b)
14
+ dir = File.expand_path("../server/views/", __FILE__)
15
+ super(dir,*a,&b)
16
+ super
17
+ end
18
+
19
+ end
20
+
21
+ app.get "/dynamicqueue" do
22
+ @queues = []
23
+ dqueues = Attr.get_dynamic_queues
24
+ dqueues.each do |k, v|
25
+ fetch = Fetch.new(:queues => ["@#{k}"], :strict => true)
26
+ expanded = fetch.queues_cmd
27
+ expanded.pop
28
+ expanded = expanded.collect {|q| q.split(":").last }
29
+ view_data = {
30
+ 'name' => k,
31
+ 'value' => Array(v).join(", "),
32
+ 'expanded' => expanded.join(", ")
33
+ }
34
+ @queues << view_data
35
+ end
36
+
37
+ @queues.sort! do |a, b|
38
+ an = a['name']
39
+ bn = b['name']
40
+ if an == 'default'
41
+ 1
42
+ elsif bn == 'default'
43
+ -1
44
+ else
45
+ an <=> bn
46
+ end
47
+ end
48
+
49
+ slim :dynamicqueue
50
+ end
51
+
52
+ app.post "/dynamicqueue" do
53
+ dynamic_queues = Array(params['queues'])
54
+ queues = {}
55
+ dynamic_queues.each do |queue|
56
+ key = queue['name']
57
+ values = queue['value'].to_s.split(',').collect{|q| q.gsub(/\s/, '') }
58
+ queues[key] = values
59
+ end
60
+ Attr.set_dynamic_queues(queues)
61
+ redirect "/dynamicqueue"
62
+ end
63
+
64
+ app.tabs["DynamicQueues"] = "dynamicqueue"
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,63 @@
1
+ h3 Dynamic Queues
2
+
3
+ p class="intro"
4
+ | The list below shows the dynamic queues currently defined. When you start
5
+ a worker with a dynamic queue key (@key_name), that key is looked up from
6
+ the list below to determine the actual queues the worker should pull from.
7
+ Wildcards (*) and negation (leading !) can be used to select the queues the
8
+ worker should process. There is always a fallback key - @default, which
9
+ workers will use if the key for that worker is empty. If both the key and
10
+ the fallback are empty, the worker defaults to processing '*'
11
+
12
+ form action="/dynamicqueue" method="POST"
13
+
14
+ table class="queues table table-hover table-bordered table-striped table-white"
15
+ thead
16
+ th Name
17
+ th Value
18
+ th Expanded
19
+ th
20
+ - @queues.each_with_index do |data, i|
21
+ tr class="line"
22
+ td
23
+ input type="text" id="input-#{{i}}-name" name="queues[][name]" value="#{{data['name']}}"
24
+ td
25
+ input type="text" id="input-#{{i}}-value" name="queues[][value]" value="#{{data['value']}}"
26
+ td class="expanded"
27
+ = data['expanded']
28
+ td
29
+ a href="#remove" class="remove" Remove
30
+
31
+ a href="#add" class="add" Add
32
+
33
+ input type="submit" value="Save"
34
+
35
+ javascript:
36
+ function markDirty()
37
+ {
38
+ $("input[type=submit]").css({border:"3px orange solid"});
39
+ }
40
+
41
+ jQuery(function($) {
42
+
43
+ $("input").live("keypress", markDirty);
44
+
45
+ $("a.add").live("click", function(e) {
46
+ e.preventDefault();
47
+ var $table = $("table.queues");
48
+ var $newRow = $table.find("tr.line:first").clone();
49
+ $newRow.find("input[type=text]").attr("value", "");
50
+ $newRow.find("td.expanded").html("")
51
+ $newRow.appendTo($table);
52
+ markDirty();
53
+ });
54
+
55
+ $("a.remove").live("click", function(e) {
56
+ e.preventDefault();
57
+ var $link = $(this);
58
+ $link.parents("tr").remove();
59
+ markDirty();
60
+ });
61
+
62
+
63
+ });