toiler 0.1.2 → 0.1.5
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 +4 -4
- data/.gitmodules +4 -0
- data/Gemfile +4 -2
- data/Gemfile.lock +49 -12
- data/celluloid-task-pooledfiber/.gitignore +9 -0
- data/celluloid-task-pooledfiber/.rspec +2 -0
- data/celluloid-task-pooledfiber/.travis.yml +5 -0
- data/celluloid-task-pooledfiber/Gemfile +11 -0
- data/celluloid-task-pooledfiber/LICENSE.txt +21 -0
- data/celluloid-task-pooledfiber/README.md +37 -0
- data/celluloid-task-pooledfiber/Rakefile +8 -0
- data/celluloid-task-pooledfiber/celluloid-task-pooled-fiber.gemspec +18 -0
- data/celluloid-task-pooledfiber/lib/celluloid/task/pooled_fiber.rb +26 -0
- data/celluloid-task-pooledfiber/lib/celluloid/util/fiber_pool.rb +95 -0
- data/celluloid-task-pooledfiber/spec/celluloid/tasks/pooled_fiber_spec.rb +5 -0
- data/celluloid-task-pooledfiber/spec/spec_helper.rb +60 -0
- data/celluloid-task-pooledfiber/spec/support/shared_examples_for_task.rb +49 -0
- data/lib/toiler/cli.rb +15 -6
- data/lib/toiler/fetcher.rb +16 -5
- data/lib/toiler/manager.rb +19 -63
- data/lib/toiler/processor.rb +17 -20
- data/lib/toiler/scheduler.rb +1 -1
- data/lib/toiler/supervisor.rb +53 -11
- data/lib/toiler/version.rb +1 -1
- data/lib/toiler.rb +4 -0
- data/toiler.gemspec +3 -2
- metadata +19 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3fb0e35e85549aef85080de3be1df4680f745d21
|
|
4
|
+
data.tar.gz: da042af1e5dd0afcefca0bb3818cca82218ecb19
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d8e30edeabdcb59975e20b5089db2fde3aaeec146cad0e039828f053039e6ee4b10df4a926783bc92e867ec8ccc903968719eb9988dd72f1a32de95eeae088f
|
|
7
|
+
data.tar.gz: 0ccafdfa4ece45209397679734eef81fe5021ca9223a997548ecb1b6438b8ab7b713fe31d4f0f45b2b75b397eae2fdb5c59bf5a880de10588663db06df5e6e70
|
data/.gitmodules
ADDED
data/Gemfile
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
source 'https://rubygems.org'
|
|
2
2
|
|
|
3
|
-
gem 'celluloid'
|
|
4
|
-
gem 'celluloid
|
|
3
|
+
# gem 'celluloid'
|
|
4
|
+
gem 'celluloid', '~> 0.17.pre15'
|
|
5
|
+
# gem 'celluloid-task-pooledfiber', github: 'celluloid/celluloid-task-pooledfiber', branch: '0.2.0-prerelease'
|
|
6
|
+
# gem 'celluloid-io'
|
|
5
7
|
gem 'aws-sdk'
|
data/Gemfile.lock
CHANGED
|
@@ -1,25 +1,63 @@
|
|
|
1
1
|
GEM
|
|
2
2
|
remote: https://rubygems.org/
|
|
3
3
|
specs:
|
|
4
|
-
aws-sdk (2.0.
|
|
5
|
-
aws-sdk-resources (= 2.0.
|
|
6
|
-
aws-sdk-core (2.0.
|
|
4
|
+
aws-sdk (2.0.42)
|
|
5
|
+
aws-sdk-resources (= 2.0.42)
|
|
6
|
+
aws-sdk-core (2.0.42)
|
|
7
7
|
builder (~> 3.0)
|
|
8
8
|
jmespath (~> 1.0)
|
|
9
9
|
multi_json (~> 1.0)
|
|
10
|
-
aws-sdk-resources (2.0.
|
|
11
|
-
aws-sdk-core (= 2.0.
|
|
10
|
+
aws-sdk-resources (2.0.42)
|
|
11
|
+
aws-sdk-core (= 2.0.42)
|
|
12
12
|
builder (3.2.2)
|
|
13
|
-
celluloid (0.
|
|
13
|
+
celluloid (0.17.0.pre15)
|
|
14
|
+
bundler
|
|
15
|
+
celluloid-essentials
|
|
16
|
+
celluloid-extras
|
|
17
|
+
celluloid-fsm
|
|
18
|
+
celluloid-pool
|
|
19
|
+
celluloid-supervision
|
|
20
|
+
dotenv
|
|
21
|
+
nenv
|
|
22
|
+
rspec-logsplit (>= 0.1.2)
|
|
14
23
|
timers (~> 4.0.0)
|
|
15
|
-
celluloid-
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
celluloid-essentials (0.20.0.pre17)
|
|
25
|
+
bundler
|
|
26
|
+
dotenv
|
|
27
|
+
nenv
|
|
28
|
+
rspec-logsplit (>= 0.1.2)
|
|
29
|
+
timers (~> 4.0.0)
|
|
30
|
+
celluloid-extras (0.1.4)
|
|
31
|
+
bundler
|
|
32
|
+
dotenv
|
|
33
|
+
nenv
|
|
34
|
+
rspec-logsplit (>= 0.1.2)
|
|
35
|
+
timers (~> 4.0.0)
|
|
36
|
+
celluloid-fsm (0.9.0.pre13)
|
|
37
|
+
bundler
|
|
38
|
+
dotenv
|
|
39
|
+
nenv
|
|
40
|
+
rspec-logsplit (>= 0.1.2)
|
|
41
|
+
timers (~> 4.0.0)
|
|
42
|
+
celluloid-pool (0.11.0.pre1)
|
|
43
|
+
bundler
|
|
44
|
+
dotenv
|
|
45
|
+
nenv
|
|
46
|
+
rspec-logsplit (>= 0.1.2)
|
|
47
|
+
timers (~> 4.0.0)
|
|
48
|
+
celluloid-supervision (0.20.0.pre6)
|
|
49
|
+
bundler
|
|
50
|
+
dotenv
|
|
51
|
+
nenv
|
|
52
|
+
rspec-logsplit (>= 0.1.2)
|
|
53
|
+
timers (~> 4.0.0)
|
|
54
|
+
dotenv (2.0.1)
|
|
18
55
|
hitimes (1.2.2)
|
|
19
56
|
jmespath (1.0.2)
|
|
20
57
|
multi_json (~> 1.0)
|
|
21
58
|
multi_json (1.11.0)
|
|
22
|
-
|
|
59
|
+
nenv (0.2.0)
|
|
60
|
+
rspec-logsplit (0.1.3)
|
|
23
61
|
timers (4.0.1)
|
|
24
62
|
hitimes
|
|
25
63
|
|
|
@@ -28,5 +66,4 @@ PLATFORMS
|
|
|
28
66
|
|
|
29
67
|
DEPENDENCIES
|
|
30
68
|
aws-sdk
|
|
31
|
-
celluloid
|
|
32
|
-
celluloid-io
|
|
69
|
+
celluloid (~> 0.17.pre15)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015 Chris Heald
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Celluloid::Task::PooledFiber
|
|
2
|
+
|
|
3
|
+
[](https://travis-ci.org/celluloid/celluloid-task-pooledfiber)
|
|
4
|
+
|
|
5
|
+
Provides a Task implementation which reuses a pool of fibers to run tasks. This improves Celluloid's performance.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'celluloid-task-pooledfiber'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And then execute:
|
|
16
|
+
|
|
17
|
+
$ bundle
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
$ gem install celluloid-task-pooledfiber
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
In your application, before starting Celluloid:
|
|
26
|
+
|
|
27
|
+
Celluloid.task_class = Celluloid::Task::PooledFiber
|
|
28
|
+
|
|
29
|
+
That's it.
|
|
30
|
+
|
|
31
|
+
## Contributing
|
|
32
|
+
|
|
33
|
+
1. Fork it ( https://github.com/cheald/celluloid-task-pooledfiber/fork )
|
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
37
|
+
5. Create a new Pull Request
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'celluloid-task-pooledfiber'
|
|
7
|
+
spec.version = '0.2.0'
|
|
8
|
+
spec.authors = ['Chris Heald']
|
|
9
|
+
spec.email = ['cheald@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'An alternate Task implementation for Celluloid which improves performance by reusing a pool of fibers to run tasks'
|
|
12
|
+
spec.description = 'An alternate Task implementation for Celluloid which improves performance by reusing a pool of fibers to run tasks'
|
|
13
|
+
spec.homepage = 'http://github.com/cheald/celluloid-task-pooledfiber'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|examples|spec|features)/}) }
|
|
17
|
+
spec.require_paths = ['lib']
|
|
18
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'celluloid' unless defined? Celluloid
|
|
2
|
+
require 'thread'
|
|
3
|
+
|
|
4
|
+
require 'celluloid/util/fiber_pool'
|
|
5
|
+
|
|
6
|
+
module Celluloid
|
|
7
|
+
class Task
|
|
8
|
+
# Tasks with a pooled Fiber backend
|
|
9
|
+
class PooledFiber < Fibered
|
|
10
|
+
def self.fiber_pool
|
|
11
|
+
@fiber_pool ||= Util::FiberPool.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create(&block)
|
|
15
|
+
queue = Thread.current[:celluloid_queue]
|
|
16
|
+
actor_system = Thread.current[:celluloid_actor_system]
|
|
17
|
+
@fiber = PooledFiber.fiber_pool.acquire do
|
|
18
|
+
Thread.current[:celluloid_role] = :actor
|
|
19
|
+
Thread.current[:celluloid_queue] = queue
|
|
20
|
+
Thread.current[:celluloid_actor_system] = actor_system
|
|
21
|
+
block.call
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
module Util
|
|
3
|
+
class FiberPool
|
|
4
|
+
attr_accessor :stats
|
|
5
|
+
|
|
6
|
+
def initialize(trim_size = 64)
|
|
7
|
+
@trim_size = trim_size
|
|
8
|
+
@pool = {}
|
|
9
|
+
@stats = { created: 0, acquired: 0, trimmed: 0, sweep_counter: 0, terminated: 0, terminated_threads: 0, sweeps: 0 }
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def acquire(&block)
|
|
14
|
+
trim
|
|
15
|
+
sweep
|
|
16
|
+
@stats[:acquired] += 1
|
|
17
|
+
fiber = fiber_pool.shift || create_fiber
|
|
18
|
+
fiber.resume(block)
|
|
19
|
+
fiber
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def create_fiber
|
|
25
|
+
pool = fiber_pool
|
|
26
|
+
@stats[:created] += 1
|
|
27
|
+
Fiber.new do |blk|
|
|
28
|
+
loop do
|
|
29
|
+
# At this point, we have a pooled fiber ready for Celluloid to call #resume on.
|
|
30
|
+
Fiber.yield
|
|
31
|
+
|
|
32
|
+
# Once Celluloid resumes the fiber, we need to execute the block the task was
|
|
33
|
+
# created with. This may call suspend/resume; that's fine.
|
|
34
|
+
blk.call
|
|
35
|
+
|
|
36
|
+
# Once Celluloid completes the task, we release this Fiber back to the pool
|
|
37
|
+
pool << Fiber.current
|
|
38
|
+
|
|
39
|
+
# ...and yield for the next #acquire call.
|
|
40
|
+
blk = Fiber.yield
|
|
41
|
+
break if blk == :terminate
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# If the fiber pool has grown too much, shift off and discard
|
|
47
|
+
# extra fibers so they may be GC'd. This isn't ideal, but it
|
|
48
|
+
# should guard against runaway resource consumption.
|
|
49
|
+
def trim
|
|
50
|
+
pool = fiber_pool
|
|
51
|
+
while pool.length > @trim_size
|
|
52
|
+
@stats[:trimmed] += 1
|
|
53
|
+
pool.shift.resume(:terminate)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def sweep
|
|
58
|
+
@mutex.synchronize do
|
|
59
|
+
@stats[:sweep_counter] += 1
|
|
60
|
+
if @stats[:sweep_counter] > 10_000
|
|
61
|
+
alive = []
|
|
62
|
+
Thread.list.each do |thread|
|
|
63
|
+
alive << thread.object_id if thread.alive? && @pool.key?(thread.object_id)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
(@pool.keys - alive).each do |thread_id|
|
|
67
|
+
@pool[thread_id].each do |_fiber|
|
|
68
|
+
@stats[:terminated] += 1
|
|
69
|
+
# We can't resume the fiber here because we might resume cross-thread
|
|
70
|
+
# TODO: How do we deal with alive fibers in a dead thread?
|
|
71
|
+
# fiber.resume(:terminate)
|
|
72
|
+
end
|
|
73
|
+
@stats[:terminated_threads] += 1
|
|
74
|
+
@pool.delete thread_id
|
|
75
|
+
end
|
|
76
|
+
@stats[:sweep_counter] = 0
|
|
77
|
+
@stats[:sweeps] += 1
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Fiber pool for this thread. Fibers can't cross threads, so we have to maintain a
|
|
83
|
+
# pool per thread.
|
|
84
|
+
#
|
|
85
|
+
# We keep our pool in an instance variable rather than in thread locals so that we
|
|
86
|
+
# can sweep out old Fibers from dead threads. This keeps live Fiber instances in
|
|
87
|
+
# a thread local from keeping the thread from being GC'd.
|
|
88
|
+
def fiber_pool
|
|
89
|
+
@mutex.synchronize do
|
|
90
|
+
@pool[Thread.current.object_id] ||= []
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
|
|
6
|
+
require 'celluloid/test'
|
|
7
|
+
require 'celluloid/rspec'
|
|
8
|
+
|
|
9
|
+
require 'celluloid/tasks/pooled_fiber'
|
|
10
|
+
|
|
11
|
+
$CELLULOID_DEBUG = true
|
|
12
|
+
|
|
13
|
+
require 'celluloid/probe'
|
|
14
|
+
|
|
15
|
+
Dir['./spec/support/*.rb'].map { |f| require f }
|
|
16
|
+
|
|
17
|
+
RSpec.configure do |config|
|
|
18
|
+
config.filter_run focus: true
|
|
19
|
+
config.run_all_when_everything_filtered = true
|
|
20
|
+
|
|
21
|
+
config.around do |ex|
|
|
22
|
+
Celluloid.actor_system = nil
|
|
23
|
+
Thread.list.each do |thread|
|
|
24
|
+
next if thread == Thread.current
|
|
25
|
+
if defined?(JRUBY_VERSION)
|
|
26
|
+
# Avoid disrupting jRuby's "fiber" threads.
|
|
27
|
+
next if /Fiber/ =~ thread.to_java.getNativeThread.get_name
|
|
28
|
+
end
|
|
29
|
+
thread.kill
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
ex.run
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
config.around actor_system: :global do |ex|
|
|
36
|
+
Celluloid.boot
|
|
37
|
+
ex.run
|
|
38
|
+
Celluloid.shutdown
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
config.around actor_system: :within do |ex|
|
|
42
|
+
Celluloid::ActorSystem.new.within do
|
|
43
|
+
ex.run
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
config.mock_with :rspec do |mocks|
|
|
48
|
+
mocks.verify_doubled_constant_names = true
|
|
49
|
+
mocks.verify_partial_doubles = true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
config.around(:each) do |example|
|
|
53
|
+
example.run
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Must be *after* the around hook above
|
|
57
|
+
require 'rspec/retry'
|
|
58
|
+
config.verbose_retry = true
|
|
59
|
+
config.default_sleep_interval = 3
|
|
60
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class MockActor
|
|
2
|
+
attr_reader :tasks
|
|
3
|
+
|
|
4
|
+
def initialize
|
|
5
|
+
@tasks = []
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def setup_thread
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
RSpec.shared_examples 'a Celluloid Task' do
|
|
13
|
+
let(:task_type) { :foobar }
|
|
14
|
+
let(:suspend_state) { :doing_something }
|
|
15
|
+
let(:actor) { MockActor.new }
|
|
16
|
+
|
|
17
|
+
subject { Celluloid.task_class.new(task_type, {}) { Celluloid::Task.suspend(suspend_state) } }
|
|
18
|
+
|
|
19
|
+
before :each do
|
|
20
|
+
Thread.current[:celluloid_actor_system] = Celluloid.actor_system
|
|
21
|
+
Thread.current[:celluloid_actor] = actor
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
after :each do
|
|
25
|
+
Thread.current[:celluloid_actor] = nil
|
|
26
|
+
Thread.current[:celluloid_actor_system] = nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'begins with status :new' do
|
|
30
|
+
expect(subject.status).to be :new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'resumes' do
|
|
34
|
+
expect(subject).to be_running
|
|
35
|
+
subject.resume
|
|
36
|
+
expect(subject.status).to eq(suspend_state)
|
|
37
|
+
subject.resume
|
|
38
|
+
expect(subject).not_to be_running
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'raises exceptions outside' do
|
|
42
|
+
task = Celluloid.task_class.new(task_type, {}) do
|
|
43
|
+
fail 'failure'
|
|
44
|
+
end
|
|
45
|
+
expect do
|
|
46
|
+
task.resume
|
|
47
|
+
end.to raise_exception('failure')
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/toiler/cli.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'singleton'
|
|
2
|
+
require 'timeout'
|
|
2
3
|
require 'optparse'
|
|
3
4
|
require 'toiler'
|
|
4
5
|
|
|
@@ -30,6 +31,7 @@ module Toiler
|
|
|
30
31
|
load_celluloid
|
|
31
32
|
|
|
32
33
|
begin
|
|
34
|
+
require 'toiler/supervisor'
|
|
33
35
|
@supervisor = Supervisor.new
|
|
34
36
|
|
|
35
37
|
while (readable_io = IO.select([self_read]))
|
|
@@ -37,8 +39,14 @@ module Toiler
|
|
|
37
39
|
handle_signal(signal)
|
|
38
40
|
end
|
|
39
41
|
rescue Interrupt
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
puts 'Received interrupt, terminating actors...'
|
|
43
|
+
begin
|
|
44
|
+
Timeout.timeout(20) do
|
|
45
|
+
@supervisor.stop
|
|
46
|
+
end
|
|
47
|
+
ensure
|
|
48
|
+
exit 0
|
|
49
|
+
end
|
|
42
50
|
end
|
|
43
51
|
end
|
|
44
52
|
|
|
@@ -54,9 +62,10 @@ module Toiler
|
|
|
54
62
|
# Celluloid can't be loaded until after we've daemonized
|
|
55
63
|
# because it spins up threads and creates locks which get
|
|
56
64
|
# into a very bad state if forked.
|
|
57
|
-
require 'celluloid/
|
|
65
|
+
require 'celluloid/current'
|
|
66
|
+
require 'celluloid/task/pooled_fiber'
|
|
67
|
+
Celluloid.task_class = Celluloid::Task::PooledFiber
|
|
58
68
|
Celluloid.logger = (Toiler.options[:verbose] ? Toiler.logger : nil)
|
|
59
|
-
require 'toiler/supervisor'
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
def daemonize
|
|
@@ -74,7 +83,7 @@ module Toiler
|
|
|
74
83
|
files_to_reopen.each do |file|
|
|
75
84
|
begin
|
|
76
85
|
file.reopen file.path, 'a+'
|
|
77
|
-
#file.sync = true
|
|
86
|
+
# file.sync = true
|
|
78
87
|
rescue ::Exception
|
|
79
88
|
end
|
|
80
89
|
end
|
|
@@ -83,7 +92,7 @@ module Toiler
|
|
|
83
92
|
File.open(Toiler.options[:logfile], 'ab') do |f|
|
|
84
93
|
io.reopen(f)
|
|
85
94
|
end
|
|
86
|
-
#io.sync = true
|
|
95
|
+
# io.sync = true
|
|
87
96
|
end
|
|
88
97
|
$stdin.reopen('/dev/null')
|
|
89
98
|
end
|
data/lib/toiler/fetcher.rb
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
module Toiler
|
|
2
2
|
class Fetcher
|
|
3
3
|
include Celluloid
|
|
4
|
-
include Celluloid::Logger
|
|
4
|
+
include Celluloid::Internals::Logger
|
|
5
5
|
|
|
6
6
|
FETCH_LIMIT = 10.freeze
|
|
7
7
|
|
|
8
|
-
attr_accessor :queue, :wait, :batch
|
|
8
|
+
attr_accessor :queue, :wait, :batch, :condition
|
|
9
9
|
|
|
10
10
|
finalizer :shutdown
|
|
11
11
|
|
|
12
|
-
def initialize(queue, client
|
|
12
|
+
def initialize(queue, client)
|
|
13
13
|
debug "Initializing Fetcher for queue #{queue}..."
|
|
14
|
+
@condition = Celluloid::Condition.new
|
|
14
15
|
@queue = Queue.new queue, client
|
|
15
16
|
@wait = Toiler.options[:wait] || 20
|
|
16
17
|
@batch = Toiler.worker_class_registry[queue].batch?
|
|
@@ -23,6 +24,15 @@ module Toiler
|
|
|
23
24
|
instance_variables.each { |iv| remove_instance_variable iv }
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
def processor_finished
|
|
28
|
+
@condition.broadcast
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def wait_for_available_processors
|
|
32
|
+
manager = Toiler.manager
|
|
33
|
+
@condition.wait if manager.dead? || manager.free_processors(queue) == 0
|
|
34
|
+
end
|
|
35
|
+
|
|
26
36
|
def poll_messages
|
|
27
37
|
# AWS limits the batch size by 10
|
|
28
38
|
options = {
|
|
@@ -31,14 +41,15 @@ module Toiler
|
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
loop do
|
|
34
|
-
count = Toiler.manager.free_processors
|
|
44
|
+
while (count = Toiler.manager.free_processors(queue.name)) == 0
|
|
45
|
+
wait_for_available_processors
|
|
46
|
+
end
|
|
35
47
|
options[:max_number_of_messages] = (batch || count > FETCH_LIMIT) ? FETCH_LIMIT : count
|
|
36
48
|
debug "Fetcher #{queue.name} retreiving messages with options: #{options.inspect}..."
|
|
37
49
|
msgs = queue.receive_messages options
|
|
38
50
|
debug "Fetcher #{queue.name} retreived #{msgs.count} messages..."
|
|
39
51
|
next if msgs.empty?
|
|
40
52
|
Toiler.manager.assign_messages queue.name, msgs
|
|
41
|
-
Toiler.manager.wait_for_available_processors queue.name
|
|
42
53
|
end
|
|
43
54
|
end
|
|
44
55
|
end
|
data/lib/toiler/manager.rb
CHANGED
|
@@ -4,91 +4,51 @@ require 'toiler/processor'
|
|
|
4
4
|
module Toiler
|
|
5
5
|
class Manager
|
|
6
6
|
include Celluloid
|
|
7
|
-
include Celluloid::Logger
|
|
8
|
-
|
|
9
|
-
attr_accessor :queues, :client
|
|
7
|
+
include Celluloid::Internals::Logger
|
|
10
8
|
|
|
11
9
|
finalizer :shutdown
|
|
12
10
|
|
|
13
11
|
def initialize
|
|
14
|
-
Toiler.set_manager current_actor
|
|
15
12
|
async.init
|
|
16
13
|
end
|
|
17
14
|
|
|
18
15
|
def init
|
|
19
16
|
debug 'Initializing manager...'
|
|
20
|
-
@queues = Toiler.worker_class_registry
|
|
21
|
-
@client = ::Aws::SQS::Client.new
|
|
22
17
|
init_workers
|
|
23
|
-
|
|
24
|
-
pool_processors
|
|
25
|
-
supervise_fetchers
|
|
18
|
+
awake_fetchers
|
|
26
19
|
debug 'Finished initializing manager...'
|
|
27
20
|
end
|
|
28
21
|
|
|
22
|
+
def awake_fetchers
|
|
23
|
+
queues.each do |q, _klass|
|
|
24
|
+
fetcher = Toiler.fetcher(q)
|
|
25
|
+
fetcher.processor_finished if fetcher && fetcher.alive? && free_processors(q) > 0
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def queues
|
|
30
|
+
Toiler.worker_class_registry
|
|
31
|
+
end
|
|
32
|
+
|
|
29
33
|
def shutdown
|
|
30
34
|
debug 'Manager shutting down...'
|
|
31
35
|
instance_variables.each { |iv| remove_instance_variable iv }
|
|
32
36
|
end
|
|
33
37
|
|
|
34
|
-
def stop
|
|
35
|
-
debug 'Manager stopping...'
|
|
36
|
-
terminate_fetchers
|
|
37
|
-
terminate_processors
|
|
38
|
-
end
|
|
39
|
-
|
|
40
38
|
def processor_finished(queue)
|
|
41
|
-
|
|
39
|
+
fetcher = Toiler.fetcher(queue)
|
|
40
|
+
fetcher.processor_finished if fetcher && fetcher.alive?
|
|
42
41
|
end
|
|
43
42
|
|
|
44
43
|
def init_workers
|
|
45
|
-
Toiler.worker_class_registry.each do |q, klass|
|
|
46
|
-
Toiler.worker_registry[q] = klass.new
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def supervise_fetchers
|
|
51
|
-
queues.each do |queue, _klass|
|
|
52
|
-
Toiler.set_fetcher queue, Fetcher.supervise(queue, client).actors.first
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def pool_processors
|
|
57
44
|
queues.each do |q, klass|
|
|
58
|
-
|
|
59
|
-
processor = if count > 1
|
|
60
|
-
Processor.pool args: [q], size: count
|
|
61
|
-
else
|
|
62
|
-
Processor.supervise(q).actors.first
|
|
63
|
-
end
|
|
64
|
-
Toiler.set_processor_pool q, processor
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def terminate_fetchers
|
|
69
|
-
queues.each do |queue, _klass|
|
|
70
|
-
fetcher = Toiler.fetcher(queue)
|
|
71
|
-
fetcher.terminate if fetcher && fetcher.alive?
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def terminate_processors
|
|
76
|
-
queues.each do |queue, _klass|
|
|
77
|
-
processor_pool = Toiler.processor_pool(queue)
|
|
78
|
-
processor_pool.terminate if processor_pool && processor_pool.alive?
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def init_conditions
|
|
83
|
-
@conditions = {}
|
|
84
|
-
queues.each do |queue, _klass|
|
|
85
|
-
@conditions[queue] = Celluloid::Condition.new
|
|
45
|
+
Toiler.worker_registry[q] = klass.new
|
|
86
46
|
end
|
|
87
47
|
end
|
|
88
48
|
|
|
89
49
|
def free_processors(queue)
|
|
90
|
-
|
|
91
|
-
|
|
50
|
+
pool = Toiler.processor_pool(queue)
|
|
51
|
+
pool && pool.alive? ? pool.idle_size : 0
|
|
92
52
|
end
|
|
93
53
|
|
|
94
54
|
def assign_messages(queue, messages)
|
|
@@ -104,12 +64,8 @@ module Toiler
|
|
|
104
64
|
debug "Manager finished assigning #{messages.count} for queue #{queue}"
|
|
105
65
|
end
|
|
106
66
|
|
|
107
|
-
def wait_for_available_processors(queue)
|
|
108
|
-
@conditions[queue].wait if free_processors(queue) == 0
|
|
109
|
-
end
|
|
110
|
-
|
|
111
67
|
def batch?(queue)
|
|
112
|
-
|
|
68
|
+
queues[queue].batch?
|
|
113
69
|
end
|
|
114
70
|
end
|
|
115
71
|
end
|
data/lib/toiler/processor.rb
CHANGED
|
@@ -4,51 +4,48 @@ require 'toiler/scheduler'
|
|
|
4
4
|
module Toiler
|
|
5
5
|
class Processor
|
|
6
6
|
include Celluloid
|
|
7
|
-
include Celluloid::Logger
|
|
7
|
+
include Celluloid::Internals::Logger
|
|
8
8
|
|
|
9
|
-
attr_accessor :queue
|
|
9
|
+
attr_accessor :queue
|
|
10
10
|
|
|
11
11
|
finalizer :shutdown
|
|
12
12
|
|
|
13
13
|
def initialize(queue)
|
|
14
14
|
debug "Initializing Processor for queue #{queue}"
|
|
15
15
|
@queue = queue
|
|
16
|
-
@scheduler = Scheduler.supervise.actors.first
|
|
17
16
|
processor_finished
|
|
18
17
|
debug "Finished initializing Processor for queue #{queue}"
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def shutdown
|
|
22
21
|
debug "Processor for queue #{queue} shutting down..."
|
|
23
|
-
|
|
22
|
+
::ActiveRecord::Base.clear_active_connections! if defined? ActiveRecord
|
|
24
23
|
instance_variables.each { |iv| remove_instance_variable iv }
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
def process(queue, sqs_msg)
|
|
28
27
|
debug "Processor #{queue} begins processing..."
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
timer = auto_visibility_timeout(queue, sqs_msg, worker.class)
|
|
28
|
+
worker = Toiler.worker_registry[queue]
|
|
29
|
+
timer = auto_visibility_timeout(queue, sqs_msg, worker.class)
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
end
|
|
31
|
+
body = get_body(worker.class, sqs_msg)
|
|
32
|
+
worker.perform(sqs_msg, body)
|
|
33
|
+
sqs_msg.delete if worker.class.auto_delete?
|
|
34
|
+
rescue StandardError => e
|
|
35
|
+
error "Processor #{queue} faild processing msg: #{e.message}\n#{e.backtrace.join("\n")}"
|
|
36
|
+
ensure
|
|
37
|
+
timer.cancel if timer
|
|
38
|
+
::ActiveRecord::Base.clear_active_connections! if defined? ActiveRecord
|
|
42
39
|
processor_finished
|
|
43
40
|
debug "Processor #{queue} finishes processing..."
|
|
44
41
|
end
|
|
45
42
|
|
|
43
|
+
private
|
|
44
|
+
|
|
46
45
|
def processor_finished
|
|
47
|
-
Toiler.manager.processor_finished queue
|
|
46
|
+
Toiler.manager.async.processor_finished queue
|
|
48
47
|
end
|
|
49
48
|
|
|
50
|
-
private
|
|
51
|
-
|
|
52
49
|
def auto_visibility_timeout(queue, sqs_msg, worker_class)
|
|
53
50
|
return unless worker_class.auto_visibility_timeout?
|
|
54
51
|
queue_visibility_timeout = Toiler.fetcher(queue).queue.visibility_timeout
|
|
@@ -57,7 +54,7 @@ module Toiler
|
|
|
57
54
|
msg.visibility_timeout = visibility_timeout
|
|
58
55
|
end
|
|
59
56
|
|
|
60
|
-
scheduler.custom_every(queue_visibility_timeout - 5, sqs_msg, queue_visibility_timeout, queue, block)
|
|
57
|
+
Toiler.scheduler(queue).custom_every(queue_visibility_timeout - 5, sqs_msg, queue_visibility_timeout, queue, block)
|
|
61
58
|
end
|
|
62
59
|
|
|
63
60
|
def get_body(worker_class, sqs_msg)
|
data/lib/toiler/scheduler.rb
CHANGED
data/lib/toiler/supervisor.rb
CHANGED
|
@@ -1,24 +1,66 @@
|
|
|
1
1
|
require 'toiler/manager'
|
|
2
2
|
|
|
3
3
|
module Toiler
|
|
4
|
-
class Supervisor
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
finalizer :shutdown
|
|
4
|
+
class Supervisor
|
|
5
|
+
attr_accessor :client, :config
|
|
8
6
|
|
|
9
7
|
def initialize
|
|
10
|
-
@
|
|
8
|
+
@client = ::Aws::SQS::Client.new
|
|
9
|
+
@config = Celluloid::Supervision::Configuration.new
|
|
10
|
+
define_manager
|
|
11
|
+
define_schedulers
|
|
12
|
+
define_processors
|
|
13
|
+
define_fetchers
|
|
14
|
+
@config.deploy
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def queues
|
|
18
|
+
Toiler.worker_class_registry
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def define_manager
|
|
22
|
+
@config.define type: Manager, as: :manager
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def define_fetchers
|
|
26
|
+
queues.each do |queue, _klass|
|
|
27
|
+
@config.define type: Fetcher, as: "fetcher_#{queue}".to_sym, args: [queue, client]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def define_schedulers
|
|
32
|
+
queues.each do |queue, _klass|
|
|
33
|
+
@config.define type: Scheduler, as: "scheduler_#{queue}".to_sym, args: []
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def define_processors
|
|
38
|
+
queues.each do |q, klass|
|
|
39
|
+
@config.define type: Celluloid::Supervision::Container::Pool,
|
|
40
|
+
as: "processor_pool_#{q}".to_sym,
|
|
41
|
+
args: [actors: Processor, size: klass.concurrency, args: [q]]
|
|
42
|
+
end
|
|
11
43
|
end
|
|
12
44
|
|
|
13
45
|
def stop
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
46
|
+
terminate_fetchers
|
|
47
|
+
terminate_processors
|
|
48
|
+
Toiler.manager.terminate if Toiler.manager && Toiler.manager.alive?
|
|
49
|
+
@config.shutdown
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def terminate_fetchers
|
|
53
|
+
queues.each do |queue, _klass|
|
|
54
|
+
fetcher = Toiler.fetcher(queue)
|
|
55
|
+
fetcher.terminate if fetcher && fetcher.alive?
|
|
56
|
+
end
|
|
17
57
|
end
|
|
18
58
|
|
|
19
|
-
def
|
|
20
|
-
|
|
21
|
-
|
|
59
|
+
def terminate_processors
|
|
60
|
+
queues.each do |queue, _klass|
|
|
61
|
+
processor_pool = Toiler.processor_pool(queue)
|
|
62
|
+
processor_pool.terminate if processor_pool && processor_pool.alive?
|
|
63
|
+
end
|
|
22
64
|
end
|
|
23
65
|
end
|
|
24
66
|
end
|
data/lib/toiler/version.rb
CHANGED
data/lib/toiler.rb
CHANGED
data/toiler.gemspec
CHANGED
|
@@ -13,9 +13,10 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
spec.license = 'LGPLv3'
|
|
14
14
|
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
|
16
|
+
spec.files += `cd celluloid-task-pooledfiber && git ls-files -z`.split("\x0").collect { |f| "celluloid-task-pooledfiber/#{f}" }
|
|
16
17
|
spec.executables << 'toiler'
|
|
17
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
18
|
-
spec.require_paths = ['lib']
|
|
19
|
+
spec.require_paths = ['lib', 'celluloid-task-pooledfiber/lib']
|
|
19
20
|
|
|
20
21
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
|
21
22
|
spec.add_development_dependency 'rake'
|
|
@@ -25,5 +26,5 @@ Gem::Specification.new do |spec|
|
|
|
25
26
|
spec.add_development_dependency 'dotenv'
|
|
26
27
|
|
|
27
28
|
spec.add_dependency 'aws-sdk', '~> 2.0.21'
|
|
28
|
-
spec.add_dependency 'celluloid', '~> 0.
|
|
29
|
+
spec.add_dependency 'celluloid', '~> 0.17.pre15'
|
|
29
30
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: toiler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sebastian Schepens
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-05-
|
|
11
|
+
date: 2015-05-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -114,14 +114,14 @@ dependencies:
|
|
|
114
114
|
requirements:
|
|
115
115
|
- - "~>"
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: 0.
|
|
117
|
+
version: 0.17.pre15
|
|
118
118
|
type: :runtime
|
|
119
119
|
prerelease: false
|
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
121
|
requirements:
|
|
122
122
|
- - "~>"
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: 0.
|
|
124
|
+
version: 0.17.pre15
|
|
125
125
|
description: Toiler is a super efficient AWS SQS thread based message processor
|
|
126
126
|
email:
|
|
127
127
|
- sebas.schep@hotmail.com
|
|
@@ -131,12 +131,26 @@ extensions: []
|
|
|
131
131
|
extra_rdoc_files: []
|
|
132
132
|
files:
|
|
133
133
|
- ".gitignore"
|
|
134
|
+
- ".gitmodules"
|
|
134
135
|
- Gemfile
|
|
135
136
|
- Gemfile.lock
|
|
136
137
|
- LICENSE
|
|
137
138
|
- README.md
|
|
138
139
|
- Rakefile
|
|
139
140
|
- bin/toiler
|
|
141
|
+
- celluloid-task-pooledfiber/.gitignore
|
|
142
|
+
- celluloid-task-pooledfiber/.rspec
|
|
143
|
+
- celluloid-task-pooledfiber/.travis.yml
|
|
144
|
+
- celluloid-task-pooledfiber/Gemfile
|
|
145
|
+
- celluloid-task-pooledfiber/LICENSE.txt
|
|
146
|
+
- celluloid-task-pooledfiber/README.md
|
|
147
|
+
- celluloid-task-pooledfiber/Rakefile
|
|
148
|
+
- celluloid-task-pooledfiber/celluloid-task-pooled-fiber.gemspec
|
|
149
|
+
- celluloid-task-pooledfiber/lib/celluloid/task/pooled_fiber.rb
|
|
150
|
+
- celluloid-task-pooledfiber/lib/celluloid/util/fiber_pool.rb
|
|
151
|
+
- celluloid-task-pooledfiber/spec/celluloid/tasks/pooled_fiber_spec.rb
|
|
152
|
+
- celluloid-task-pooledfiber/spec/spec_helper.rb
|
|
153
|
+
- celluloid-task-pooledfiber/spec/support/shared_examples_for_task.rb
|
|
140
154
|
- lib/toiler.rb
|
|
141
155
|
- lib/toiler/cli.rb
|
|
142
156
|
- lib/toiler/core_ext.rb
|
|
@@ -160,6 +174,7 @@ post_install_message:
|
|
|
160
174
|
rdoc_options: []
|
|
161
175
|
require_paths:
|
|
162
176
|
- lib
|
|
177
|
+
- celluloid-task-pooledfiber/lib
|
|
163
178
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
164
179
|
requirements:
|
|
165
180
|
- - ">="
|