shoryuken-later 0.0.2 → 0.0.3

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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MzBhOTRiYmE3MTc2MWU5NzE4OTg3YjUzNjg4N2I2OGJjYWE1ZWEyNQ==
4
+ ZmRmMzRlZjhlM2I4YTM3NjhjNDUxOTQxYzYwZTE1ZjZiNGNiMWQwNA==
5
5
  data.tar.gz: !binary |-
6
- MDUxYzI1OWIyYjBmYTZhYmVmZDNhYzEwZTFhYzhkMjI1YmJlZmVlMw==
6
+ ZTQwODFkZDI4YmE4NGM2MzZiZjFkM2NmNTViZTk4OWQ3NGI2NzVhYQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZjBiOTEwMGEwMGIxMmI2NDRkOTAyNmIzODU4MzNlNDAzMzZmZDRhNWE5ODM0
10
- MDkzZWYxMzY4MDcwMzEwZDdlNmI5ODY1ZjU5NmJmYTc4NTljMTgwMDRjZGY1
11
- NWM3NTljMTExNzA1MmY3NjdlMDE2OGJlYzFjMzg1YmYxYTc4OWU=
9
+ NjYxMDExNjQzYjhhNTJmZWE3ZmZmZWMxYjRmN2IwYjU1OTU0Nzg4NTIyOGQx
10
+ YzM0NzUzNjZjYzAzOTBjNDZiZGEzYTk3ZDU0MjdmM2ZlNmI0OTg5ZWQ4YjM2
11
+ NWU1Y2U0NDZmMTZkY2QwYjE5MTJlYzJjMmI1MzA5NzVkNGE2YTE=
12
12
  data.tar.gz: !binary |-
13
- YmU4ZDFlMjJjYWY4ZThiNzdiNjJlMGFlNzY3Y2EwMGNmY2NlNTc4YTIyYjUy
14
- ZmY5ZTIxYWU0NDViYjI2NzhlMDNhOGEwYzMyZDVjM2ZhNTk1NzE4ZGJjZDkx
15
- MGQ3ZjA2YTYzZGUwY2I3ZGFhOWM4N2QzODg4YzAyMzlkZmJjZWI=
13
+ MzRiYjI0NDUxMGExNzVmMjA3ZDRlNDlkM2E0NzgzZTc1ZGM5YWE2YzY0OGQy
14
+ Y2Q0N2QwZjEwMzM2YmUxYzMxYjBkYzUxOWQ0ZjFkNjMwOTNhNjM2MzkxMzdi
15
+ ZWRiOGRlYjk1MTIzM2IzYjU1ZWJkOWJlNWI2OWQxY2JlMWFiNzg=
data/README.md CHANGED
@@ -1,26 +1,15 @@
1
1
  # shoryuken-later
2
2
 
3
3
  A scheduling plugin for [Shoryuken](https://github.com/phstc/shoryuken) that uses [Dynamo DB](https://aws.amazon.com/dynamodb/)
4
- to schedule messages arbitrarily far into the future.
4
+ to delay messages arbitrarily far into the future.
5
5
 
6
6
  ## Features
7
7
 
8
- ### Integration with Shoryuken::Worker
9
-
10
- A new method named `perform_later` is added to `Shoryuken::Worker` allowing messages to be delayed arbitrarily far into the future. If the delay is 15 minutes or less, then the message is enqueued into the specified SQS `:queue` as usual. Otherwise, the message is inserted into the specified DynamoDB `:schedule_table`.
11
-
12
- ```ruby
13
- require 'shoryuken-later'
8
+ ### Supports distributed architectures
14
9
 
15
- class MyWorker
16
- include Shoryuken::Worker
17
-
18
- shoryuken_options queue: 'default', schedule_table: 'default_schedule'
19
- end
10
+ An SQS message is *only* queued if a _conditional_ delete of the DDB item is successful. This eliminates any potential race condition, so if more than one `shoryuken-later` process is polling the same schedule table then no redundant SQS messages will be queued.
20
11
 
21
- # Schedules a message to be processed 30 minutes from now.
22
- MyWorker.perform_later(Time.now + 30 * 60, 'Foobar')
23
- ```
12
+ NOTE: You shouldn't really _need_ to run more than one process, but if you do it will be safe.
24
13
 
25
14
  ### One or more schedule tables
26
15
 
@@ -38,21 +27,19 @@ later:
38
27
  You can use the same configuration file for both `Shoryuken` and `Shoryuken::Later`, because the new configuration options are namespaced.
39
28
 
40
29
  ```yaml
41
- # This key is used by both Shoryuken and Shoryuken::Later
30
+ # These keys are used by both Shoryuken and Shoryuken::Later
42
31
  aws:
43
32
  access_key_id: ... # or <%= ENV['AWS_ACCESS_KEY_ID'] %>
44
33
  secret_access_key: ... # or <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
45
34
  region: us-east-1 # or <%= ENV['AWS_REGION'] %>
35
+ logfile: some/path/to/file.log
46
36
 
47
37
  # This key is only used by Shoryuken::Later
48
38
  later:
49
39
  delay: 5 * 60 # How frequently to poll the schedule table, in seconds.
40
+ pidfile: some/path/to/file.pid
50
41
  tables:
51
42
  - table1
52
-
53
- # These keys are used by both Shoryuken and Shoryuken::Later
54
- logfile: some/path/to/file.log
55
- pidfile: some/path/to/file.pid
56
43
 
57
44
  # These keys are only used by Shoryuken
58
45
  concurrency: 3
@@ -60,4 +47,102 @@ delay: 0
60
47
  queues:
61
48
  - [queue1, 1]
62
49
  - [queue2, 2]
63
- ```
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ### Starting the schedule poller
55
+
56
+ Start the `shoryuken-later` schedule poller with a command like:
57
+
58
+ ```shell
59
+ bundle exec shoryuken-later --config shoryuken.yml
60
+ ```
61
+
62
+ Run it as a daemon inside of your Rails app with a command like:
63
+
64
+ ```shell
65
+ bundle exec shoryuken-later --config shoryuken.yml --rails --daemon
66
+ ```
67
+
68
+ [Command-line options](https://github.com/joekhoobyar/shoryuken-later/wiki/Command-line-options)
69
+
70
+
71
+ ### Integration with ActiveJob
72
+
73
+ A custom ActiveJob adapter can used to support delaying messages arbitrarily far into the future.
74
+
75
+ ```ruby
76
+ # config/application.rb
77
+ config.active_job.queue_adapter = :shoryuken_later
78
+ ```
79
+
80
+ When you use the `:shoryuken_later` queue adapter, jobs to be performed farther than 15 minutes into the future (by setting the `wait` or `wait_until` ActiveJob options), will be inserted into the *default* schedule table. You can set the default schedule table in an initializer.
81
+
82
+ ```ruby
83
+ # config/initializers/shoryuken_later.rb
84
+ Shoryuken::Later.default_table = "#{Rails.env}_myapp_later"
85
+ ```
86
+
87
+
88
+ ### Integration with Shoryuken::Worker
89
+
90
+ A new method named `perform_later` is added to `Shoryuken::Worker` allowing messages to be delayed arbitrarily far into the future. If the delay is 15 minutes or less, then the message is enqueued into the specified SQS `:queue` as usual. Otherwise, the message is inserted into the specified DynamoDB `:schedule_table`.
91
+
92
+ ```ruby
93
+ require 'shoryuken-later'
94
+
95
+ class MyWorker
96
+ include Shoryuken::Worker
97
+
98
+ shoryuken_options queue: 'default', schedule_table: 'default_schedule'
99
+ end
100
+
101
+ # Schedules a message to be processed 30 minutes from now.
102
+ MyWorker.perform_later(Time.now + 30 * 60, 'Foobar')
103
+ ```
104
+
105
+
106
+ ## Requirements
107
+
108
+ Ruby 1.9 or greater.
109
+
110
+ ## Installation
111
+
112
+ Add this line to your application's Gemfile:
113
+
114
+ ```ruby
115
+ gem 'shoryuken-later'
116
+ ```
117
+
118
+ Or to get the latest updates:
119
+
120
+ ```ruby
121
+ gem 'shoryuken-later', github: 'joekhoobyar/shoryuken-later', branch: 'master'
122
+ ```
123
+
124
+ And then execute:
125
+
126
+ $ bundle
127
+
128
+ Or install it yourself as:
129
+
130
+ $ gem install shoryuken-later
131
+
132
+ ## Documentation
133
+
134
+ Learn about using Shoryuken::Later at the [Shoryuken::Later Wiki](https://github.com/joekhoobyar/shoryuken-later/wiki).
135
+
136
+ Learn about using Shoryuken at the [Shoryuken Wiki](https://github.com/phstc/shoryuken/wiki).
137
+
138
+ ## Credits
139
+
140
+ [Pablo Cantero](https://github.com/phstc), creator of [Shoryuken](https://github.com/phstc/shoryuken), and [everybody who contributed to it](https://github.com/phstc/shoryuken/graphs/contributors). I borrowed a lot of code from Shoryuken itself as a shortcut to making this gem.
141
+
142
+ ## Contributing
143
+
144
+ 1. Fork it ( https://github.com/joekhoobyar/shoryuken-later/fork )
145
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
146
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
147
+ 4. Push to the branch (`git push origin my-new-feature`)
148
+ 5. Create a new Pull Request
data/Rakefile CHANGED
File without changes
File without changes
@@ -7,6 +7,7 @@ require 'singleton'
7
7
  require 'optparse'
8
8
  require 'erb'
9
9
  require 'shoryuken/later'
10
+ require 'timers'
10
11
 
11
12
  module Shoryuken
12
13
  module Later
@@ -14,12 +15,10 @@ module Shoryuken
14
15
  include Shoryuken::Util
15
16
  include Singleton
16
17
 
17
- attr_accessor :launcher
18
-
19
18
  def run(args)
20
19
  self_read, self_write = IO.pipe
21
20
 
22
- %w[INT TERM USR1 USR2 TTIN].each do |sig|
21
+ %w[INT TERM USR1 USR2].each do |sig|
23
22
  trap sig do
24
23
  self_write.puts(sig)
25
24
  end
@@ -34,38 +33,49 @@ module Shoryuken
34
33
  validate!
35
34
  daemonize
36
35
  write_pid
37
- load_celluloid
38
-
39
- require 'shoryuken/later/launcher'
40
- @launcher = Shoryuken::Later::Launcher.new
41
-
42
- begin
43
- launcher.run
44
-
45
- while readable_io = IO.select([self_read])
46
- signal = readable_io.first[0].gets.strip
47
- handle_signal(signal)
36
+
37
+ Shoryuken::Logging.with_context '[later]' do
38
+ logger.info 'Starting'
39
+
40
+ # Initialize the timers and poller.
41
+ @timers = Timers.new
42
+ require 'shoryuken/later/poller'
43
+ @pollers = Shoryuken::Later.tables.map{|tbl| Poller.new(tbl) }
44
+
45
+ begin
46
+ # Poll for items on startup, and every :poll_delay
47
+ poll_tables
48
+ @timers.every(Shoryuken::Later.poll_delay){ poll_tables }
49
+
50
+ # Loop watching for signals and firing off of timers
51
+ while @timers
52
+ interval = @timers.wait_interval
53
+ readable, writable = IO.select([self_read], nil, nil, interval)
54
+ if readable
55
+ handle_signal readable.first.gets.strip
56
+ else
57
+ @timers.fire
58
+ end
59
+ end
60
+ rescue Interrupt
61
+ @timers.cancel
62
+ exit 0
48
63
  end
49
- rescue Interrupt
50
- launcher.stop(shutdown: true)
51
- exit 0
52
64
  end
53
65
  end
66
+
67
+ protected
68
+
69
+ def poll_tables
70
+ logger.debug "Polling schedule tables"
71
+ @pollers.each do |poller|
72
+ poller.poll
73
+ end
74
+ logger.debug "Polling done"
75
+ end
54
76
 
55
77
  private
56
78
 
57
- def load_celluloid
58
- raise "Celluloid cannot be required until here, or it will break Shoryuken::Later's daemonization" if defined?(::Celluloid) && Shoryuken::Later.options[:daemon]
59
-
60
- # Celluloid can't be loaded until after we've daemonized
61
- # because it spins up threads and creates locks which get
62
- # into a very bad state if forked.
63
- require 'celluloid/autostart'
64
- Celluloid.logger = (Shoryuken::Later.options[:verbose] ? Shoryuken::Later.logger : nil)
65
-
66
- require 'shoryuken/later/manager'
67
- end
68
-
69
79
  def load_rails
70
80
  # Adapted from: https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/cli.rb
71
81
 
@@ -79,6 +89,7 @@ module Shoryuken
79
89
  ::Rails::Application.initializer "shoryuken-later.eager_load" do
80
90
  ::Rails.application.config.eager_load = true
81
91
  end
92
+ require 'shoryuken/later/active_job_adapter' if defined?(::ActiveJob)
82
93
  require File.expand_path("config/environment.rb")
83
94
  end
84
95
 
@@ -117,7 +128,7 @@ module Shoryuken
117
128
  end
118
129
 
119
130
  def write_pid
120
- if path = Shoryuken::Later.options[:pidfile]
131
+ if path = Shoryuken::Later.options[:later][:pidfile]
121
132
  File.open(path, 'w') do |f|
122
133
  f.puts Process.pid
123
134
  end
@@ -125,7 +136,7 @@ module Shoryuken
125
136
  end
126
137
 
127
138
  def parse_options(argv)
128
- opts = {}
139
+ opts = {later: {}}
129
140
 
130
141
  @parser = OptionParser.new do |o|
131
142
  o.on '-d', '--daemon', 'Daemonize process' do |arg|
@@ -153,7 +164,7 @@ module Shoryuken
153
164
  end
154
165
 
155
166
  o.on '-P', '--pidfile PATH', 'Path to pidfile' do |arg|
156
- opts[:pidfile] = arg
167
+ opts[:later][:pidfile] = arg
157
168
  end
158
169
 
159
170
  o.on '-v', '--verbose', 'Print more verbose output' do |arg|
@@ -181,28 +192,10 @@ module Shoryuken
181
192
  case sig
182
193
  when 'USR1'
183
194
  logger.info "Received USR1, will soft shutdown down"
184
-
185
- launcher.stop
186
-
187
- exit 0
188
- when 'TTIN'
189
- Thread.list.each do |thread|
190
- logger.info "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
191
- if thread.backtrace
192
- logger.info thread.backtrace.join("\n")
193
- else
194
- logger.info "<no backtrace available>"
195
- end
196
- end
197
-
198
- idle = launcher.manager.instance_variable_get(:@idle).size
199
- busy = launcher.manager.instance_variable_get(:@busy).size
200
- tables = launcher.manager.instance_variable_get(:@tables)
201
-
202
- logger.info "Idle: #{idle}, Busy: #{busy}, Polled Tables: #{tables.join(', ')}"
195
+ @timers.cancel
196
+ @timers = nil
203
197
  else
204
198
  logger.info "Received #{sig}, will shutdown down"
205
-
206
199
  raise Interrupt
207
200
  end
208
201
  end
@@ -215,8 +208,10 @@ module Shoryuken
215
208
 
216
209
  config = options[:config_file] ? parse_config(options[:config_file]).deep_symbolize_keys : {}
217
210
 
211
+ Shoryuken::Later.options[:later].merge!(config.delete(:later) || {})
218
212
  Shoryuken::Later.options.merge!(config)
219
213
 
214
+ Shoryuken::Later.options[:later].merge!(options.delete(:later) || {})
220
215
  Shoryuken::Later.options.merge!(options)
221
216
 
222
217
  # Tables from command line options take precedence...
File without changes
@@ -3,16 +3,12 @@ require 'json'
3
3
  module Shoryuken
4
4
  module Later
5
5
  class Poller
6
- include Celluloid
7
6
  include Shoryuken::Util
8
7
 
9
8
  attr_reader :table_name
10
9
 
11
- def initialize(manager, table_name)
12
- @manager = manager
10
+ def initialize(table_name)
13
11
  @table_name = table_name
14
-
15
- @manager.async.poller_ready(@table_name, self)
16
12
  end
17
13
 
18
14
  def poll
@@ -25,12 +21,10 @@ module Shoryuken
25
21
  while item = next_item
26
22
  id = item.attributes['id']
27
23
  logger.info "Found message #{id} from '#{@table_name}'"
28
- defer do
29
- if sent_msg = process_item(item)
30
- logger.debug { "Enqueued message #{id} from '#{@table_name}' as #{sent_msg.id}" }
31
- else
32
- logger.debug { "Skipping already queued message #{id} from '#{@table_name}'" }
33
- end
24
+ if sent_msg = process_item(item)
25
+ logger.debug { "Enqueued message #{id} from '#{@table_name}' as #{sent_msg.id}" }
26
+ else
27
+ logger.debug { "Skipping already queued message #{id} from '#{@table_name}'" }
34
28
  end
35
29
  end
36
30
 
@@ -39,8 +33,6 @@ module Shoryuken
39
33
  logger.error "Error fetching message: #{ex}"
40
34
  logger.error ex.backtrace.first
41
35
  end
42
-
43
- @manager.async.poller_done(@table_name, self)
44
36
  end
45
37
  end
46
38
 
@@ -1,5 +1,5 @@
1
1
  module Shoryuken
2
2
  module Later
3
- VERSION = '0.0.2'
3
+ VERSION = '0.0.3'
4
4
  end
5
5
  end
File without changes
@@ -20,13 +20,13 @@ Gem::Specification.new do |spec|
20
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.required_ruby_version = '>= 1.9.3'
23
+ spec.required_ruby_version = '>= 1.9.2'
24
24
 
25
25
  spec.add_development_dependency "bundler", '>= 1.3.5'
26
26
  spec.add_development_dependency "rake", '~> 10.0'
27
27
  spec.add_development_dependency "rspec", '~> 3.0', '< 3.1'
28
28
 
29
29
  spec.add_dependency "aws-sdk-v1"
30
- spec.add_dependency "celluloid", "~> 0.15.2"
31
- spec.add_dependency "shoryuken", "~> 0.0.4"
30
+ spec.add_dependency "timers", "~> 1.1.0"
31
+ spec.add_dependency "shoryuken", "= 0.0.4"
32
32
  end
File without changes
@@ -1,9 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'shoryuken/later/poller'
3
- require 'shoryuken/later/manager'
4
3
 
5
4
  describe Shoryuken::Later::Poller do
6
- let(:manager) { double Shoryuken::Later::Manager, poller_ready: nil, poller_done: nil }
7
5
  let(:ddb_table) { double 'DynamoDb Table' }
8
6
  let(:ddb_items) { double 'Table Items' }
9
7
  let(:table) { 'shoryuken_later' }
@@ -17,38 +15,26 @@ describe Shoryuken::Later::Poller do
17
15
  end
18
16
 
19
17
  before do
20
- allow(manager).to receive(:async).and_return(manager)
21
18
  allow(Shoryuken::Later::Client).to receive(:tables).with(table).and_return(ddb_table)
22
19
  end
23
20
 
24
21
  subject do
25
- described_class.new(manager, table)
22
+ described_class.new(table)
26
23
  end
27
24
 
28
- describe '#initialize' do
29
- it 'informs the manager that the poller is ready' do
30
- expect(manager).to receive(:poller_ready).once
31
-
32
- subject.inspect
33
- subject.inspect
34
- end
35
- end
36
-
37
25
  describe '#poll' do
38
26
  it 'pulls items from #next_item, and processes with #process_item' do
39
27
  items = [ddb_item]
40
28
  expect_any_instance_of(described_class).to receive(:next_item).twice { items.pop }
41
29
  expect_any_instance_of(described_class).to receive(:process_item).once.with(ddb_item)
42
- expect(manager).to receive(:poller_done).once
43
30
 
44
31
  subject.poll
45
32
  end
46
33
 
47
- it 'informs the manager after polling is done' do
34
+ it 'does not call #process_item when there are no items' do
48
35
  items = []
49
36
  expect_any_instance_of(described_class).to receive(:next_item).once { items.pop }
50
37
  expect_any_instance_of(described_class).not_to receive(:process_item)
51
- expect(manager).to receive(:poller_done).once
52
38
 
53
39
  subject.poll
54
40
  end
File without changes
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'bundler/setup'
2
2
  Bundler.setup
3
3
 
4
- require 'celluloid'
5
4
  require 'shoryuken-later'
6
5
  require 'json'
7
6
 
@@ -16,7 +15,6 @@ if File.exists? options_file
16
15
  end
17
16
 
18
17
  Shoryuken.logger.level = Logger::UNKNOWN
19
- Celluloid.logger.level = Logger::UNKNOWN
20
18
 
21
19
  # For Ruby 1.9
22
20
  module Kernel
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shoryuken-later
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Khoobyar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-10 00:00:00.000000000 Z
11
+ date: 2015-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -73,31 +73,31 @@ dependencies:
73
73
  - !ruby/object:Gem::Version
74
74
  version: '0'
75
75
  - !ruby/object:Gem::Dependency
76
- name: celluloid
76
+ name: timers
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - ~>
80
80
  - !ruby/object:Gem::Version
81
- version: 0.15.2
81
+ version: 1.1.0
82
82
  type: :runtime
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - ~>
87
87
  - !ruby/object:Gem::Version
88
- version: 0.15.2
88
+ version: 1.1.0
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: shoryuken
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - ~>
93
+ - - '='
94
94
  - !ruby/object:Gem::Version
95
95
  version: 0.0.4
96
96
  type: :runtime
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
- - - ~>
100
+ - - '='
101
101
  - !ruby/object:Gem::Version
102
102
  version: 0.0.4
103
103
  description: ! "\n This gem provides a scheduling plugin (using Dynamo DB) for
@@ -120,8 +120,6 @@ files:
120
120
  - lib/shoryuken/later/active_job_adapter.rb
121
121
  - lib/shoryuken/later/cli.rb
122
122
  - lib/shoryuken/later/client.rb
123
- - lib/shoryuken/later/launcher.rb
124
- - lib/shoryuken/later/manager.rb
125
123
  - lib/shoryuken/later/poller.rb
126
124
  - lib/shoryuken/later/version.rb
127
125
  - lib/shoryuken/later/worker.rb
@@ -142,7 +140,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
140
  requirements:
143
141
  - - ! '>='
144
142
  - !ruby/object:Gem::Version
145
- version: 1.9.3
143
+ version: 1.9.2
146
144
  required_rubygems_version: !ruby/object:Gem::Requirement
147
145
  requirements:
148
146
  - - ! '>='
@@ -1,42 +0,0 @@
1
- # All of this has been "borrowed" from Shoryuken.
2
-
3
- # @see Shoryuken::Launcher
4
- module Shoryuken
5
- module Later
6
- class Launcher
7
- include Celluloid
8
- include Shoryuken::Util
9
-
10
- trap_exit :actor_died
11
-
12
- attr_accessor :manager
13
-
14
- def initialize
15
- @manager = Shoryuken::Later::Manager.new_link
16
-
17
- @done = false
18
- end
19
-
20
- def stop(options = {})
21
- watchdog('Later::Launcher#stop') do
22
- @done = true
23
-
24
- manager.async.stop(shutdown: !!options[:shutdown], timeout: Shoryuken::Later.options[:timeout])
25
- manager.wait(:shutdown)
26
- end
27
- end
28
-
29
- def run
30
- watchdog('Later::Launcher#run') do
31
- manager.async.start
32
- end
33
- end
34
-
35
- def actor_died(actor, reason)
36
- return if @done
37
- logger.warn 'Shoryuken::Later died due to the following error, cannot recover, process exiting'
38
- exit 1
39
- end
40
- end
41
- end
42
- end
@@ -1,138 +0,0 @@
1
- # Most of this has been "borrowed" from Shoryuken, but then repurposed for periodic polling.
2
-
3
- # @see Shoryuken::Manager
4
- require 'set'
5
- require 'shoryuken/later/poller'
6
-
7
- module Shoryuken
8
- module Later
9
- class Manager
10
- include Celluloid
11
- include Shoryuken::Util
12
-
13
- def initialize
14
- @tables = Shoryuken::Later.tables.dup.uniq
15
-
16
- @done = false
17
-
18
- @idle = Set.new([])
19
- @busy = Set.new([])
20
- @timers = {}
21
-
22
- @tables.each{|table| Poller.supervise_as :"poller-#{table}", current_actor, table }
23
- end
24
-
25
- def start
26
- logger.info 'Starting'
27
-
28
- # Start a poller for every table being polled.
29
- @tables.each do |table|
30
- dispatch table
31
-
32
- # Save the timer so it can be cancelled at shutdown.
33
- @timers[table] = every(Shoryuken::Later.poll_delay) { dispatch table }
34
- end
35
- end
36
-
37
- def stop(options = {})
38
- watchdog('Later::Manager#stop died') do
39
- @done = true
40
-
41
- @timers.each_value{|timer| timer.cancel if timer }
42
- @timers.clear
43
-
44
- logger.info { "Shutting down #{@idle.size} idle poller(s)" }
45
-
46
- @idle.each do |name|
47
- poller = Actor[name] and poller.alive? and poller.terminate
48
- end
49
- @idle.clear
50
-
51
- if @busy.empty?
52
- return after(0) { signal(:shutdown) }
53
- end
54
-
55
- if options[:shutdown]
56
- hard_shutdown_in(options[:timeout])
57
- else
58
- soft_shutdown(options[:timeout])
59
- end
60
- end
61
- end
62
-
63
- def poller_done(table, poller)
64
- watchdog('Later::Manager#poller_done died') do
65
- logger.debug { "Poller done for '#{table}'" }
66
-
67
- name = :"poller-#{table}"
68
- @busy.delete name
69
-
70
- if stopped?
71
- poller.terminate if poller.alive?
72
- else
73
- @idle << name
74
- end
75
- end
76
- end
77
-
78
- def poller_ready(table, poller)
79
- watchdog('Later::Manager#poller_ready died') do
80
- logger.debug { "Poller for '#{table}' ready" }
81
-
82
- name = :"poller-#{table}"
83
- @busy.delete name
84
- @idle << name
85
- end
86
- end
87
-
88
- def stopped?
89
- @done
90
- end
91
-
92
- private
93
-
94
- def dispatch(table)
95
- name = :"poller-#{table}"
96
-
97
- # Only start polling if the poller is idle.
98
- if ! stopped? && @idle.include?(name)
99
- @idle.delete(name)
100
- @busy << name
101
-
102
- Actor[name].async.poll
103
- end
104
- end
105
-
106
- def soft_shutdown(delay)
107
- logger.info { "Waiting for #{@busy.size} busy pollers" }
108
-
109
- if @busy.size > 0
110
- after(delay) { soft_shutdown(delay) }
111
- else
112
- after(0) { signal(:shutdown) }
113
- end
114
- end
115
-
116
- def hard_shutdown_in(delay)
117
- logger.info { "Waiting for #{@busy.size} busy pollers" }
118
- logger.info { "Pausing up to #{delay} seconds to allow pollers to finish..." }
119
-
120
- after(delay) do
121
- watchdog("Later::Manager#hard_shutdown_in died") do
122
- if @busy.size > 0
123
- logger.info { "Hard shutting down #{@busy.size} busy pollers" }
124
-
125
- @busy.each do |busy|
126
- if poller = Actor[busy]
127
- t = poller.bare_object.actual_work_thread
128
- t.raise Shutdown if poller.alive?
129
- end
130
- end
131
- end
132
- after(0) { signal(:shutdown) }
133
- end
134
- end
135
- end
136
- end
137
- end
138
- end