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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d01f9405ae62e985f25de5ff0a2f79f67c43c2b2
4
- data.tar.gz: 3bdc7a8ced52a7ef39eb5e8af90c84076f3e57df
3
+ metadata.gz: 3fb0e35e85549aef85080de3be1df4680f745d21
4
+ data.tar.gz: da042af1e5dd0afcefca0bb3818cca82218ecb19
5
5
  SHA512:
6
- metadata.gz: 2fc8d6adbff7c7c5ad263e97171a37e9eb70748ca01bbf314ad23401b2e71332eea1da523938ba2248a71ffc507c677a4d1ed9cc1c1dc88cc574d29af6d5fa5d
7
- data.tar.gz: bc210eac97b7cf07c5903e65ac9865de9bb8595539e252254b855b9654de7281ebb8a34ae5773e5bc074c7e59db544c73e168b3bc75b293445ab6d7ffb4410de
6
+ metadata.gz: 7d8e30edeabdcb59975e20b5089db2fde3aaeec146cad0e039828f053039e6ee4b10df4a926783bc92e867ec8ccc903968719eb9988dd72f1a32de95eeae088f
7
+ data.tar.gz: 0ccafdfa4ece45209397679734eef81fe5021ca9223a997548ecb1b6438b8ab7b713fe31d4f0f45b2b75b397eae2fdb5c59bf5a880de10588663db06df5e6e70
data/.gitmodules ADDED
@@ -0,0 +1,4 @@
1
+ [submodule "celluloid-task-pooledfiber"]
2
+ path = celluloid-task-pooledfiber
3
+ url = git@github.com:celluloid/celluloid-task-pooledfiber.git
4
+ branch = 0.2.0-prerelease
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'celluloid'
4
- gem 'celluloid-io'
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.41)
5
- aws-sdk-resources (= 2.0.41)
6
- aws-sdk-core (2.0.41)
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.41)
11
- aws-sdk-core (= 2.0.41)
10
+ aws-sdk-resources (2.0.42)
11
+ aws-sdk-core (= 2.0.42)
12
12
  builder (3.2.2)
13
- celluloid (0.16.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-io (0.16.2)
16
- celluloid (>= 0.16.0)
17
- nio4r (>= 1.1.0)
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
- nio4r (1.1.0)
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,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ - rbx-2
5
+ - jruby
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'celluloid', github: 'celluloid/celluloid', branch: '0.17.0-prerelease'
4
+
5
+ gemspec
6
+
7
+ group :test do
8
+ gem 'rake'
9
+ gem 'rspec', '~> 3.2'
10
+ gem 'rspec-retry'
11
+ end
@@ -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
+ [![Build Status](https://travis-ci.org/celluloid/celluloid-task-pooledfiber.svg)](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,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |spec|
5
+ spec.pattern = 'spec/**/*_spec.rb'
6
+ spec.rspec_opts = ['--color --format documentation']
7
+ end
8
+ task default: :spec
@@ -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,5 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Celluloid::Task::PooledFiber, actor_system: :within do
4
+ it_behaves_like 'a Celluloid Task'
5
+ 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
- @supervisor.stop
41
- exit 0
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/autostart'
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
@@ -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 = nil)
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 queue.name
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
@@ -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
- init_conditions
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
- @conditions[queue].broadcast
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
- count = klass.concurrency
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
- return 1 unless Toiler.processor_pool(queue).respond_to? :idle_size
91
- Toiler.processor_pool(queue).idle_size
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
- Toiler.worker_class_registry[queue].batch?
68
+ queues[queue].batch?
113
69
  end
114
70
  end
115
71
  end
@@ -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, :scheduler
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
- scheduler.terminate if scheduler && scheduler.alive?
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
- exclusive do
30
- worker = Toiler.worker_registry[queue]
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
- begin
34
- body = get_body(worker.class, sqs_msg)
35
- worker.perform(sqs_msg, body)
36
- sqs_msg.delete if worker.class.auto_delete?
37
- ensure
38
- timer.cancel if timer
39
- ::ActiveRecord::Base.clear_active_connections! if defined? ActiveRecord
40
- end
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)
@@ -1,7 +1,7 @@
1
1
  module Toiler
2
2
  class Scheduler
3
3
  include Celluloid
4
- include Celluloid::Logger
4
+ include Celluloid::Internals::Logger
5
5
 
6
6
  execute_block_on_receiver :custom_every
7
7
 
@@ -1,24 +1,66 @@
1
1
  require 'toiler/manager'
2
2
 
3
3
  module Toiler
4
- class Supervisor < Celluloid::SupervisionGroup
5
- include Celluloid
6
-
7
- finalizer :shutdown
4
+ class Supervisor
5
+ attr_accessor :client, :config
8
6
 
9
7
  def initialize
10
- @manager = Manager.new
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
- return unless @manager.alive?
15
- @manager.stop
16
- @manager.terminate
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 shutdown
20
- @manager.terminate if @manager.alive?
21
- instance_variables.each { |iv| remove_instance_variable iv }
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
@@ -1,3 +1,3 @@
1
1
  module Toiler
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.5'
3
3
  end
data/lib/toiler.rb CHANGED
@@ -53,6 +53,10 @@ module Toiler
53
53
  Celluloid::Actor["processor_pool_#{queue}".to_sym] = val
54
54
  end
55
55
 
56
+ def scheduler(queue)
57
+ Celluloid::Actor["scheduler_#{queue}".to_sym]
58
+ end
59
+
56
60
  def manager
57
61
  Celluloid::Actor[:manager]
58
62
  end
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.16.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.2
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-07 00:00:00.000000000 Z
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.16.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.16.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
  - - ">="