sidecloq 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d0f6f9c7d36841096162c3d1ec7775220e58438
4
- data.tar.gz: 4d11bcc58feabca4f13c0cf05f6a8d34257e5d29
3
+ metadata.gz: c42da59e13d5181bbdaed20b903ea9ec5215d341
4
+ data.tar.gz: 80bcf519574b7937caa39c862305fedcec503587
5
5
  SHA512:
6
- metadata.gz: 328c80251970cb9899a5106394a5a0c206ce5317fb88be4d4f01092e6521d7f8fcd487535403d6bd917636739be852b0a0051f51ea25ab739dfbebef040ce894
7
- data.tar.gz: 3a956934c7415080ca8a68664471cab8e86707eb914b3ae4864f58b2ada039c47e7559c76054982f0e4780f58d87172079e8a4426be107f2d41e579e9c571bbd
6
+ metadata.gz: d285cf602bc72794c0f01854550fd9a6315d2ab740633e67880e3ec2704f327fbbed220c8f46c013112525d406a8e0ff3609ac68bdac5b9b7ea9dfb98088bd9e
7
+ data.tar.gz: 6e6af9ab2fb27b80aa41020bb8d9dbd48b967cde2c9d5e8f943b25be7974d27fdb5c6a30127455c336d96c4a5b330e3b8497e01a903277ee4c8533d0a3a0c0ea
data/.travis.yml CHANGED
@@ -16,3 +16,6 @@ matrix:
16
16
  allow_failures:
17
17
  - rvm: ruby-head
18
18
  - rvm: jruby-head
19
+ addons:
20
+ code_climate:
21
+ repo_token: dee2fdcb3562533038a22e862c0fc91a6f1fa38730cd2b77177655e5b9507c83
data/Gemfile CHANGED
@@ -6,3 +6,5 @@ platforms :mri do
6
6
  gem 'pry'
7
7
  gem 'minitest-utils'
8
8
  end
9
+
10
+ gem 'codeclimate-test-reporter', group: :test, require: false
data/README.md CHANGED
@@ -1,13 +1,41 @@
1
+ ![Sidecloq](clock_a_clock_on_the_side.png)
2
+
1
3
  # Sidecloq
2
4
 
3
5
  [![Build Status](https://travis-ci.org/mattyr/sidecloq.svg)](https://travis-ci.org/mattyr/sidecloq)
6
+ [![Gem Version](https://badge.fury.io/rb/sidecloq.svg)](https://badge.fury.io/rb/sidecloq)
7
+ [![Code Climate](https://codeclimate.com/github/mattyr/sidecloq/badges/gpa.svg)](https://codeclimate.com/github/mattyr/sidecloq)
8
+ [![Test Coverage](https://codeclimate.com/github/mattyr/sidecloq/badges/coverage.svg)](https://codeclimate.com/github/mattyr/sidecloq/coverage)
9
+ [![Dependency Status](https://gemnasium.com/mattyr/sidecloq.svg)](https://gemnasium.com/mattyr/sidecloq)
4
10
 
5
- Another recurring job extension for Sidekiq.
11
+ Recurring / Periodic / Scheduled / Cron job extension for
12
+ [Sidekiq](https://github.com/mperham/sidekiq)
6
13
 
7
14
  ## Why
8
15
 
9
- TODO: design principles, differences, inspiration (sidetiq,
10
- sidekiq-scheduler, resque-scheduler)
16
+ There are several options for running periodic tasks with Sidekiq,
17
+ including [Sidetiq](https://github.com/tobiassvn/sidetiq),
18
+ [sidekiq-scheduler](https://github.com/Moove-it/sidekiq-scheduler),
19
+ [sidekiq-cron](https://github.com/ondrejbartas/sidekiq-cron), as well as
20
+ [Sidekiq Pro](http://sidekiq.org/products/pro). Each tackles the
21
+ problem slightly differently. Sidecloq is inspired by various facets
22
+ of these projects, as well as
23
+ [resque-scheduler](https://github.com/resque/resque-scheduler). I urge
24
+ you to take a look at all of these options to see what works best for
25
+ you.
26
+
27
+ Sidecloq is:
28
+
29
+ - **Lightweight:** Celluloid is not required. This coincides well with
30
+ Sidekiq 4, which no longer uses Celluloid.
31
+ - **Clean:** Sidecloq leverages only the public API of Sidekiq, and does
32
+ not pollute the Sidekiq namespace.
33
+ - **Easy to deploy:** Sidecloq boots with all Sideqik processes,
34
+ automatically. Leader election ensures only one process enqueues
35
+ jobs, and a new leader is automatically chosen should the current
36
+ leader die.
37
+ - **Easy to configure:** Schedule configuration is done in YAML, using
38
+ the familiar cron syntax. No special DSL or job class mixins required.
11
39
 
12
40
  ## Installation
13
41
 
@@ -17,20 +45,61 @@ Add this line to your application's Gemfile:
17
45
  gem 'sidecloq'
18
46
  ```
19
47
 
20
- Configure Sidecloq alongside your Sidekiq config. If using Rails, and
21
- your schedule is located at config/sidecloq.yml, you don't have to do
22
- anything (ie, omit this whole configuration block).
48
+ ## Configuration
49
+
50
+ ### Quickstart
51
+
52
+ Tell Sidecloq where your schedule file is located:
23
53
 
24
54
  ```ruby
25
55
  Sidcloq.configure do |config|
26
- config[:schedule_file] =
27
- File.join(Rails.root, "config/myschedule.yml")
56
+ config[:schedule_file] = "path/to/myschedule.yml"
28
57
  end
29
58
  ```
59
+ ### Rails
30
60
 
31
- TODO: configuration options
61
+ If using Rails, and your schedule is located at config/sidecloq.yml,
62
+ Sidecloq will find the schedule automatically (ie, you don't have to use
63
+ the above configuration block).
32
64
 
33
- TODO: schedule file format (like resque-scheduler)
65
+ ## Schedule file format
66
+
67
+ ### Example:
68
+
69
+ ```yaml
70
+ my_scheduled_job: # a unique name for this schedule
71
+ class: Jobs::DoWork # the job class
72
+ cron: "* * * * *" # cron formatted schedule
73
+ queue: "queue_name" # Sidekiq queue for job
74
+
75
+ my_other_scheduled_job:
76
+ class: Jobs::AnotherClassName
77
+ cron: "1 1 * * *"
78
+ queue: "a_different_queue"
79
+ ```
80
+
81
+ ### Rails
82
+
83
+ If using Rails, you can nest the schedules under top-level environment
84
+ keys, and Sidecloq will select the correct group based on the Rails
85
+ environment. This is useful for development/staging scenarios. For
86
+ example:
87
+
88
+ ```yaml
89
+ production:
90
+ # these will only run in production
91
+ my_scheduled_job:
92
+ class: Jobs::ClassName
93
+ cron: "* * * * *"
94
+ queue: "queue_name"
95
+
96
+ staging:
97
+ # this will only run in staging
98
+ my_other_scheduled_job:
99
+ class: Jobs::AnotherClassName
100
+ cron: "1 1 * * *"
101
+ queue: "a_different_queue"
102
+ ```
34
103
 
35
104
  ## Web Extension
36
105
 
@@ -41,7 +110,11 @@ require 'sidekiq/web'
41
110
  require 'sidecloq/web'
42
111
  ```
43
112
 
44
- TODO: screenshot/directions
113
+ This will add a "Recurring" tab to the sidekiq web ui, where the loaded
114
+ schedules are displayed. You can enqueue a job immediately by clicking
115
+ it's corresponding "Enqueue now" button.
116
+
117
+ ![Sidecloq web ui extension screenshot](screenshot.png)
45
118
 
46
119
  ## Contributing
47
120
 
@@ -50,5 +123,3 @@ Bug reports and pull requests are welcome.
50
123
  ## License
51
124
 
52
125
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
53
-
54
- TODO: project links
data/Rakefile CHANGED
@@ -1,21 +1,38 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
7
  t.test_files = FileList['test/**/test_*.rb']
8
8
  end
9
9
 
10
- task :default => :test
10
+ task default: :test
11
11
 
12
- desc "Run the Sidekiq web interface (w/Sidecloq)"
12
+ desc 'Run the Sidekiq web interface (w/Sidecloq)'
13
13
  task :web do
14
14
  require 'sidekiq'
15
15
  require 'sidecloq'
16
16
 
17
17
  Sidekiq.configure_client do |config|
18
- config.redis = { url: 'redis://localhost:6379/0', size: 1, namespace: 'sidecloq' }
18
+ config.redis = {
19
+ url: 'redis://localhost:6379/0',
20
+ size: 1,
21
+ namespace: 'sidecloq'
22
+ }
23
+ end
24
+
25
+ # throw a fake job in
26
+ Sidecloq.configure do |config|
27
+ sched = Sidecloq::Schedule.from_hash({
28
+ my_scheduled_job: {
29
+ class: 'DoWork',
30
+ cron: '* * * * *',
31
+ queue: 'default'
32
+ }
33
+ })
34
+ sched.save_redis
35
+ config[:schedule] = sched
19
36
  end
20
37
 
21
38
  require 'sidekiq/web'
Binary file
@@ -3,9 +3,9 @@ module Sidecloq
3
3
  class Locker
4
4
  include Utils
5
5
 
6
- DEFAULT_LOCK_KEY = "sidecloq_leader_lock"
6
+ DEFAULT_LOCK_KEY = 'sidecloq_leader_lock'
7
7
 
8
- def initialize(options={})
8
+ def initialize(options = {})
9
9
  # we keep a connection from the pool by default
10
10
  @redis = options[:redis] || Sidekiq.redis_pool.checkout
11
11
  @key = options[:lock_key] || DEFAULT_LOCK_KEY
@@ -24,33 +24,36 @@ module Sidecloq
24
24
  end
25
25
 
26
26
  def stop(timeout = nil)
27
- if @check_task
28
- logger.debug("Stopping locker check task")
29
- @check_task.shutdown
30
- @check_task.wait_for_termination(timeout)
31
- logger.debug("Stopped locker check task")
32
- end
27
+ return unless @check_task
28
+
29
+ logger.debug('Stopping locker check task')
30
+ @check_task.shutdown
31
+ @check_task.wait_for_termination(timeout)
32
+ logger.debug('Stopped locker check task')
33
33
  end
34
34
 
35
- def has_lock?
35
+ def locked?
36
36
  @obtained_lock.set?
37
37
  end
38
38
 
39
39
  private unless $TESTING
40
40
 
41
41
  def start
42
- logger.debug("Starting locker check task")
43
- @check_task = Concurrent::TimerTask.new(execution_interval: @check_interval, run_now: true) do
44
- get_or_refresh_lock
42
+ logger.debug('Starting locker check task')
43
+ @check_task = Concurrent::TimerTask.new(
44
+ execution_interval: @check_interval,
45
+ run_now: true
46
+ ) do
47
+ try_to_get_or_refresh_lock
45
48
  end
46
49
  @check_task.execute
47
50
  end
48
51
 
49
- def get_or_refresh_lock
52
+ def try_to_get_or_refresh_lock
50
53
  # redlock is in ms, not seconds
51
54
  @lock = @lock_manager.lock(@key, @ttl * 1000, extend: @lock)
52
55
  @obtained_lock.set if @lock
53
- logger.debug("Leader lock #{"not " if !@lock}held")
56
+ logger.debug("Leader lock #{'not ' unless @lock}held")
54
57
  @lock
55
58
  end
56
59
  end
@@ -13,24 +13,24 @@ module Sidecloq
13
13
 
14
14
  def run
15
15
  @thread = Thread.new do
16
- logger.info("Runner starting")
16
+ logger.info('Runner starting')
17
17
  @locker.with_lock do
18
18
  # i am the leader
19
- logger.info("Obtained leader lock")
19
+ logger.info('Obtained leader lock')
20
20
  @scheduler.run
21
21
  end
22
- logger.info("Runner ending")
22
+ logger.info('Runner ending')
23
23
  end
24
24
  end
25
25
 
26
26
  def stop(timeout = nil)
27
- logger.debug("Stopping runner")
28
- if @locker.has_lock?
27
+ logger.debug('Stopping runner')
28
+ if @locker.locked?
29
29
  @scheduler.stop(timeout)
30
30
  @locker.stop(timeout)
31
31
  end
32
32
  @thread.join if @thread
33
- logger.debug("Stopped runner")
33
+ logger.debug('Stopped runner')
34
34
  end
35
35
 
36
36
  private unless $TESTING
@@ -32,23 +32,20 @@ module Sidecloq
32
32
  end
33
33
 
34
34
  def self.from_hash(hash)
35
- if defined?(Rails) && hash.key?(Rails.env)
36
- hash = hash[Rails.env]
37
- end
35
+ hash = hash[Rails.env] if defined?(Rails) && hash.key?(Rails.env)
38
36
 
39
- specs = hash.inject({}) do |memo, (name, spec)|
37
+ specs = hash.each_with_object({}) do |(name, spec), memo|
40
38
  memo[name] = spec.dup.tap do |s|
41
39
  s['class'] = name unless spec.key?('class') || spec.key?(:class)
42
40
  s['args'] = s['args'] || s[:args] || []
43
41
  end
44
- memo
45
42
  end
46
43
 
47
44
  new(specs)
48
45
  end
49
46
 
50
47
  def save_yaml(filename)
51
- File.open(filename,'w') do |h|
48
+ File.open(filename, 'w') do |h|
52
49
  h.write @job_specs.to_yaml
53
50
  end
54
51
  end
@@ -10,13 +10,13 @@ module Sidecloq
10
10
 
11
11
  # run queues jobs per their schedules, blocking forever
12
12
  def run
13
- logger.info("Loading schedules into redis")
13
+ logger.info('Loading schedules into redis')
14
14
  sync_with_redis
15
- logger.info("Starting scheduler")
15
+ logger.info('Starting scheduler')
16
16
  load_schedule_into_rufus
17
- logger.debug("Joining rufus thread")
17
+ logger.debug('Joining rufus thread')
18
18
  rufus.join
19
- logger.debug("Scheduler run ended")
19
+ logger.debug('Scheduler run ended')
20
20
  end
21
21
 
22
22
  def stop(timeout = nil)
@@ -33,7 +33,7 @@ module Sidecloq
33
33
  rufus.shutdown(:wait)
34
34
  end
35
35
  rufus.join
36
- logger.info("Stopped scheduler")
36
+ logger.info('Stopped scheduler')
37
37
  end
38
38
 
39
39
  private unless $TESTING
@@ -47,7 +47,7 @@ module Sidecloq
47
47
  end
48
48
 
49
49
  def load_schedule_into_rufus
50
- logger.debug("Scheduling jobs")
50
+ logger.debug('Scheduling jobs')
51
51
  @schedule.job_specs.each do |name, spec|
52
52
  load_into_rufus(name, spec)
53
53
  end
@@ -71,14 +71,14 @@ module Sidecloq
71
71
 
72
72
  # failed enqeueuing should not b0rk stuff
73
73
  begin
74
- enqueue_job!(name, spec)
74
+ enqueue_job!(spec)
75
75
  rescue => e
76
76
  logger.info "error enqueuing #{name} - #{e.class.name}: #{e.message}"
77
77
  end
78
78
  end
79
79
 
80
80
  # can raise exceptions, but shouldn't
81
- def enqueue_job!(name, spec)
81
+ def enqueue_job!(spec)
82
82
  Sidekiq::Client.push(spec)
83
83
  end
84
84
  end
@@ -1,5 +1,8 @@
1
1
  module Sidecloq
2
+ # Useful stuff
2
3
  module Utils
4
+ # Sets the Sidekiq::Logging context automatically with direct calls to
5
+ # *.logger
3
6
  class ContextLogger
4
7
  def initialize(ctx)
5
8
  @context = ctx
@@ -25,9 +28,7 @@ module Sidecloq
25
28
  def will_never_run(cronline)
26
29
  # look for non-existent day of month
27
30
  split = cronline.split(/\s+/)
28
- if split.length > 3 &&
29
- split[2] =~ /\d+/
30
- split[3] =~ /\d+/
31
+ if split.length > 3 && split[2] =~ /\d+/ && split[3] =~ /\d+/
31
32
 
32
33
  month = split[3].to_i
33
34
  day = split[2].to_i
@@ -1,3 +1,3 @@
1
1
  module Sidecloq
2
- VERSION = "0.1.0"
2
+ VERSION = '0.1.1'
3
3
  end
data/lib/sidecloq/web.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  module Sidecloq
2
+ # Plugin for sidekiq-web
2
3
  module Web
3
4
  VIEW_PATH = File.expand_path('../../../web/views', __FILE__)
4
5
 
5
6
  def self.registered(app)
6
-
7
7
  app.get '/recurring' do
8
8
  @schedule = Schedule.from_redis
9
9
 
@@ -11,12 +11,13 @@ module Sidecloq
11
11
  end
12
12
 
13
13
  app.post '/recurring/:name/enqueue' do |name|
14
+ # rubocop:disable Lint/AssignmentInCondition
14
15
  if spec = Sidecloq::Schedule.from_redis.job_specs[name]
15
16
  Sidekiq::Client.push(spec)
16
17
  end
18
+ # rubocop:enableLint/AssignmentInCondition
17
19
  redirect "#{root_path}recurring"
18
20
  end
19
-
20
21
  end
21
22
  end
22
23
  end
data/lib/sidecloq.rb CHANGED
@@ -11,6 +11,7 @@ require 'sidecloq/scheduler'
11
11
  require 'sidecloq/runner'
12
12
  require 'sidecloq/version'
13
13
 
14
+ # Sideloq provides a lightweight recurring job scheduler for sidekiq
14
15
  module Sidecloq
15
16
  def self.install
16
17
  Sidekiq.configure_server do |config|
@@ -33,13 +34,12 @@ module Sidecloq
33
34
  end
34
35
 
35
36
  def self.configure
36
- yield self
37
+ yield options
37
38
  end
38
39
 
39
40
  def self.startup
40
- unless options[:scheduler]
41
- options[:schedule] ||= extract_schedule
42
- end
41
+ options[:schedule] ||= extract_schedule unless options[:scheduler]
42
+
43
43
  @runner = Runner.new(options)
44
44
  @runner.run
45
45
  end
@@ -55,13 +55,13 @@ module Sidecloq
55
55
  return options[:schedule] if options[:schedule]
56
56
 
57
57
  # try for a file
58
- options[:schedule_file] ||= "config/sidecloq.yml"
59
- if File.exists?(options[:schedule_file])
58
+ options[:schedule_file] ||= 'config/sidecloq.yml'
59
+ if File.exist?(options[:schedule_file])
60
60
  return Schedule.from_yaml(options[:schedule_file])
61
61
  elsif defined?(Rails)
62
62
  # try rails-root-relative
63
63
  full_path = File.join(Rails.root, options[:schedule_file])
64
- if File.exists?(full_path)
64
+ if File.exist?(full_path)
65
65
  options[:schedule_file] = full_path
66
66
  return Schedule.from_yaml(options[:schedule_file])
67
67
  end
data/screenshot.png ADDED
Binary file
data/sidetoq.gemspec CHANGED
@@ -4,30 +4,30 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'sidecloq/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "sidecloq"
7
+ spec.name = 'sidecloq'
8
8
  spec.version = Sidecloq::VERSION
9
- spec.authors = ["Matt Robinson"]
10
- spec.email = ["robinson.matty@gmail.com"]
9
+ spec.authors = ['Matt Robinson']
10
+ spec.email = ['robinson.matty@gmail.com']
11
11
 
12
- spec.summary = %q{Recurring jobs for Sidekiq}
12
+ spec.summary = 'Recurring jobs for Sidekiq'
13
13
  spec.description = spec.summary
14
- spec.homepage = "http://github.com/mattyr/sidecloq"
15
- spec.license = "MIT"
14
+ spec.homepage = 'http://github.com/mattyr/sidecloq'
15
+ spec.license = 'MIT'
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "bin"
18
+ spec.bindir = 'bin'
19
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
21
21
 
22
- spec.add_dependency "sidekiq", "~> 4.0.1"
23
- spec.add_dependency "redlock", "~> 0.1.2"
22
+ spec.add_dependency 'sidekiq', '~> 4.0.1'
23
+ spec.add_dependency 'redlock', '~> 0.1.2'
24
24
  # mimics some dev dependencies of sidekiq:
25
- spec.add_dependency "concurrent-ruby"
26
- spec.add_dependency "sinatra", "~> 1.4", ">= 1.4.6"
27
- spec.add_dependency "redis-namespace", "~> 1.5", ">= 1.5.2"
28
- spec.add_dependency "multi_json", "~> 1.11"
29
- spec.add_dependency "rufus-scheduler", "~> 3.1", ">= 3.1.10"
25
+ spec.add_dependency 'concurrent-ruby'
26
+ spec.add_dependency 'sinatra', '~> 1.4', '>= 1.4.6'
27
+ spec.add_dependency 'redis-namespace', '~> 1.5', '>= 1.5.2'
28
+ spec.add_dependency 'multi_json', '~> 1.11'
29
+ spec.add_dependency 'rufus-scheduler', '~> 3.1', '>= 3.1.10'
30
30
 
31
- spec.add_development_dependency "rake", "~> 10.0"
32
- spec.add_development_dependency "minitest"
31
+ spec.add_development_dependency 'rake', '~> 10.0'
32
+ spec.add_development_dependency 'minitest'
33
33
  end
@@ -5,8 +5,7 @@
5
5
  <thead>
6
6
  <tr>
7
7
  <th>Name</th>
8
- <th>Description</th>
9
- <th>Interval</th>
8
+ <th>Cron</th>
10
9
  <th>Class</th>
11
10
  <th>Queue</th>
12
11
  <th>Arguments</th>
@@ -18,7 +17,6 @@
18
17
  <% @schedule.job_specs.each do |name, job_spec| %>
19
18
  <tr>
20
19
  <td><%= name %></td>
21
- <td><%= job_spec['description'] %></td>
22
20
  <td><%= job_spec.fetch 'cron', job_spec['every'] %></td>
23
21
  <td><%= job_spec['class'] %></td>
24
22
  <td>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidecloq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Robinson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-23 00:00:00.000000000 Z
11
+ date: 2015-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -167,6 +167,7 @@ files:
167
167
  - LICENSE.txt
168
168
  - README.md
169
169
  - Rakefile
170
+ - clock_a_clock_on_the_side.png
170
171
  - lib/sidecloq.rb
171
172
  - lib/sidecloq/locker.rb
172
173
  - lib/sidecloq/runner.rb
@@ -175,6 +176,7 @@ files:
175
176
  - lib/sidecloq/utils.rb
176
177
  - lib/sidecloq/version.rb
177
178
  - lib/sidecloq/web.rb
179
+ - screenshot.png
178
180
  - sidetoq.gemspec
179
181
  - web/views/recurring.erb
180
182
  homepage: http://github.com/mattyr/sidecloq