shoryuken-later 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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