sidekiq-dejavu 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: db1fce872db7e71c8d3fa6b819efd20a2c82ec1e
4
+ data.tar.gz: d777847b463d571f05dacfc66d37802fa804f9d4
5
+ SHA512:
6
+ metadata.gz: 41ef48ae1fdee04195a2cf5d3456b9d0825ebe5c084a8f6f8eb7595e522dad1a531aec15ae5ee0894fe23bf9dd03cc0bcc720cc43e8aa9bde174e7c516285707
7
+ data.tar.gz: cf5bcf503c9199f5721ec7e3539d9c77d91a61d1a90a71ca91ed4f51131de62b834a7d07cfb3d8d556b2fb3e8d09605fc878ce46315e41d2b4821d20a55fa322
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sidekiq-dejavu.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Felix Buenemann
2
+
3
+ MIT License
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,78 @@
1
+ # Sidekiq::Dejavu
2
+
3
+ Dejavu is a clockless scheduler that uses Sidekiq's built-in scheduling.
4
+
5
+ Most scheduling solutions for Sidekiq require either a separate cron like process
6
+ or run an internal clock thread inside sidekiq server to manage schedules.
7
+
8
+ Dejavu uses Sidekiq's internal scheduling, like `Sidekiq::Worker.perform_in`
9
+ and `Sidekiq::Worker.perform_at` so it doesn't need its own clock process/thread
10
+ and integrates nicely with Sidekiq without duplicating any functionality.
11
+
12
+ Scheduled jobs can be controlled through the Scheduled page in the Sidekiq::Web UI.
13
+
14
+ ## Note
15
+
16
+ This is **ALPHA** quality code and doesn't yet have any specs, so make sure you test it well.
17
+ Requires Sidekiq 3.0 or later. Compatibility with older versions could probably be added.
18
+
19
+ ## Known Bugs
20
+
21
+ - Incompatible with sidekiq-unique-jobs (jobs don't get scheduled)
22
+ - It's currently possible to have multiple workers from the same schedule running in parallel
23
+ and all re-scheduling themselves. This is because jobs get re-scheduled if there is no existing
24
+ schedule with the same name, but only the scheduled jobs queue is inspected.
25
+ Depending on the intervals used it is possible that the job gets scheduled and run again before
26
+ the other jobs can see the scheduled job, so they get re-scheduled as well.
27
+
28
+ ## Installation
29
+
30
+ Add this line to your application's Gemfile:
31
+
32
+ gem 'sidekiq-dejavu'
33
+
34
+ And then execute:
35
+
36
+ $ bundle
37
+
38
+ Or install it yourself as:
39
+
40
+ $ gem install sidekiq-dejavu
41
+
42
+ ## Usage
43
+
44
+ You can configure schedule tasks in your config/sidekiq.yml:
45
+
46
+ :schedule:
47
+ hello_world:
48
+ interval: 10
49
+ class: HelloWorldWorker
50
+ queue: hello
51
+ retry: false
52
+ backtrace: false
53
+ args:
54
+ - Hello
55
+ - World
56
+ cleanup:
57
+ interval: '30 5 * * *' # every day at 5:30
58
+ class: HelloWorldWorker
59
+ queue: hello
60
+ retry: false
61
+ backtrace: false
62
+ args:
63
+ - Hello
64
+ - World
65
+
66
+ Interval can be specified in seconds or as a cron expression, including vixie cron syntax.
67
+
68
+ If a job fails or takes longer than an interval it will be retried at the next interval. In most cases you should set jour jobs to `retry: false` to keep Sidekiq's retries from competing with the scheduled jobs.
69
+
70
+ You also need to set all options for the job, because currently a workers `sidekiq_options` are ignored. The minium required options for a schedule are `interval` and `class`. The same worker can be scheduled multiple times by using different schedule names (keys) in the config.
71
+
72
+ ## Contributing
73
+
74
+ 1. Fork it ( https://github.com/felixbuenemann/sidekiq-dejavu/fork )
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,18 @@
1
+ require "sidekiq/dejavu"
2
+
3
+ Sidekiq.configure_server do |config|
4
+ config.server_middleware do |chain|
5
+ chain.add Sidekiq::Dejavu::Middleware::Server::Scheduler
6
+ end
7
+
8
+ config.on(:startup) do
9
+ schedules = config.options.fetch(:schedule, {})
10
+ Sidekiq.logger.debug "Sidekiq::Dejavu: schedules: #{schedules.inspect}"
11
+ if schedules.empty?
12
+ Sidekiq.logger.warn "Sidekiq::Dejavu: No schedule found."
13
+ else
14
+ Sidekiq.logger.info "Sidekiq::Dejavu: Loading schedules #{schedules.keys.join ','}."
15
+ Sidekiq::Dejavu::Manager.new(schedules).reload_schedule!
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,4 @@
1
+ require 'sidekiq/dejavu/version'
2
+ require 'sidekiq/dejavu/helper'
3
+ require 'sidekiq/dejavu/manager'
4
+ require 'sidekiq/dejavu/middleware/server/scheduler'
@@ -0,0 +1,19 @@
1
+ require 'parse-cron'
2
+
3
+ module Sidekiq
4
+ module Dejavu
5
+ module Helper
6
+ def next_timestamp(interval, time = Time.now)
7
+ CronParser.new(interval).next(time).to_f
8
+ rescue ArgumentError
9
+ time.to_f + interval.to_f
10
+ end
11
+
12
+ def next_randomized_timestamp(interval, time = Time.now)
13
+ CronParser.new(interval).next(time).to_f
14
+ rescue ArgumentError
15
+ time.to_f + Random.rand(interval.to_f)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,53 @@
1
+ module Sidekiq
2
+ module Dejavu
3
+ class Manager
4
+ include Helper
5
+
6
+ attr_reader :schedules
7
+
8
+ def initialize(schedules)
9
+ @schedules = schedules
10
+ end
11
+
12
+ def reload_schedule!
13
+ clear_changed_schedules
14
+ add_new_schedules
15
+ end
16
+
17
+ private
18
+
19
+ def clear_changed_schedules
20
+ scheduled_jobs.each do |job|
21
+ item = job.item
22
+ name = item['schedule']
23
+ schedule_options = schedules[name]
24
+ schedule_options['args'] = Array(schedule_options['args'])
25
+ item_options = item.select { |k,v| schedule_options.keys.include? k }
26
+
27
+ if item_options != schedule_options
28
+ Sidekiq.logger.info "Clearing schedule #{name} (config changed)."
29
+ job.delete
30
+ else
31
+ schedules.delete(name)
32
+ end
33
+ end
34
+ end
35
+
36
+ def add_new_schedules
37
+ schedules.each do |name, options|
38
+ args = Array(options['args'])
39
+ interval = options['interval']
40
+ first_run = next_randomized_timestamp(interval)
41
+ job = options.merge('args' => args, 'schedule' => name, 'at' => first_run)
42
+
43
+ Sidekiq.logger.info "Scheduling #{name} for first run at #{Time.at first_run}."
44
+ Sidekiq::Client.push(job)
45
+ end
46
+ end
47
+
48
+ def scheduled_jobs
49
+ Sidekiq::ScheduledSet.new.select { |job| schedules.keys.include? job.item['schedule'] }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,50 @@
1
+ module Sidekiq
2
+ module Dejavu
3
+ module Middleware
4
+ module Server
5
+ class Scheduler
6
+ include Helper
7
+
8
+ attr_reader :schedules, :options
9
+
10
+ def initialize(options = {})
11
+ # Sidekiq.logger.info "Intialized #{self.class} with options #{options.inspect}"
12
+ @schedules = Sidekiq::ScheduledSet.new
13
+ @options = options
14
+ end
15
+
16
+ def call(worker, item, queue)
17
+ start = Time.now
18
+ interval = item['interval']
19
+ schedule = item['schedule']
20
+
21
+ yield
22
+
23
+ ensure
24
+ if interval && not_already_scheduled?(schedule)
25
+ time = relative_to_start? ? start : Time.now
26
+ schedule_next_run(worker, item, interval, time)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def not_already_scheduled?(schedule)
33
+ !!schedule && schedules.select{ |job| job.item['schedule'] == schedule }.empty?
34
+ end
35
+
36
+ def schedule_next_run(worker, item, interval, from_time)
37
+ timestamp = next_timestamp(interval, from_time)
38
+ Sidekiq.logger.info "Scheduling #{worker.class} for #{Time.at timestamp}"
39
+ item['at'] = timestamp
40
+ worker.class.client_push(item)
41
+ end
42
+
43
+ def relative_to_start?
44
+ !!options[:relative]
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ module Sidekiq
2
+ module Dejavu
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sidekiq/dejavu/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sidekiq-dejavu"
8
+ spec.version = Sidekiq::Dejavu::VERSION
9
+ spec.authors = ["Felix Buenemann"]
10
+ spec.email = ["buenemann@louis.info"]
11
+ spec.summary = %q{Dejavu is a clockless scheduler that uses Sidekiq's built-in scheduling}
12
+ spec.description = %q{Dejavu uses Sidkiq's internal scheduling so it doesn't need its own clock and integrates nicely with Sidekiq's native scheduled jobs.}
13
+ spec.homepage = "https://github.com/felixbuenemann/sidekiq-dejavu"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "sidekiq", "~> 3.0"
22
+ spec.add_dependency "parse-cron", "~> 0.1.4"
23
+ spec.add_development_dependency "bundler", "~> 1.6"
24
+ spec.add_development_dependency "rake"
25
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidekiq-dejavu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Felix Buenemann
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sidekiq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: parse-cron
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.4
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Dejavu uses Sidkiq's internal scheduling so it doesn't need its own clock
70
+ and integrates nicely with Sidekiq's native scheduled jobs.
71
+ email:
72
+ - buenemann@louis.info
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/sidekiq-dejavu.rb
83
+ - lib/sidekiq/dejavu.rb
84
+ - lib/sidekiq/dejavu/helper.rb
85
+ - lib/sidekiq/dejavu/manager.rb
86
+ - lib/sidekiq/dejavu/middleware/server/scheduler.rb
87
+ - lib/sidekiq/dejavu/version.rb
88
+ - sidekiq-dejavu.gemspec
89
+ homepage: https://github.com/felixbuenemann/sidekiq-dejavu
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.2.2
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Dejavu is a clockless scheduler that uses Sidekiq's built-in scheduling
113
+ test_files: []