toiler 0.3.1.beta1 → 0.3.1.beta2

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.
@@ -1,64 +1,64 @@
1
- module Toiler
2
- module Aws
3
- # SQS Message abstraction
4
- # Provides methods for querying and acting on a SQS message
5
- class Message
6
- attr_accessor :client, :queue_url, :data
7
-
8
- def initialize(client, queue_url, data)
9
- @client = client
10
- @queue_url = queue_url
11
- @data = data
12
- end
13
-
14
- def delete
15
- client.delete_message(
16
- queue_url: queue_url,
17
- receipt_handle: data.receipt_handle
18
- )
19
- end
20
-
21
- def change_visibility(options)
22
- client.change_message_visibility(
23
- options.merge(queue_url: queue_url, receipt_handle: receipt_handle)
24
- )
25
- end
26
-
27
- def visibility_timeout=(timeout)
28
- client.change_message_visibility(
29
- queue_url: queue_url,
30
- receipt_handle: data.receipt_handle,
31
- visibility_timeout: timeout
32
- )
33
- end
34
-
35
- def message_id
36
- data.message_id
37
- end
38
-
39
- def receipt_handle
40
- data.receipt_handle
41
- end
42
-
43
- def md5_of_body
44
- data.md5_of_body
45
- end
46
-
47
- def body
48
- data.body
49
- end
50
-
51
- def attributes
52
- data.attributes
53
- end
54
-
55
- def md5_of_message_attributes
56
- data.md5_of_message_attributes
57
- end
58
-
59
- def message_attributes
60
- data.message_attributes
61
- end
62
- end
63
- end
64
- end
1
+ module Toiler
2
+ module Aws
3
+ # SQS Message abstraction
4
+ # Provides methods for querying and acting on a SQS message
5
+ class Message
6
+ attr_accessor :client, :queue_url, :data
7
+
8
+ def initialize(client, queue_url, data)
9
+ @client = client
10
+ @queue_url = queue_url
11
+ @data = data
12
+ end
13
+
14
+ def delete
15
+ client.delete_message(
16
+ queue_url: queue_url,
17
+ receipt_handle: data.receipt_handle
18
+ )
19
+ end
20
+
21
+ def change_visibility(options)
22
+ client.change_message_visibility(
23
+ options.merge(queue_url: queue_url, receipt_handle: receipt_handle)
24
+ )
25
+ end
26
+
27
+ def visibility_timeout=(timeout)
28
+ client.change_message_visibility(
29
+ queue_url: queue_url,
30
+ receipt_handle: data.receipt_handle,
31
+ visibility_timeout: timeout
32
+ )
33
+ end
34
+
35
+ def message_id
36
+ data.message_id
37
+ end
38
+
39
+ def receipt_handle
40
+ data.receipt_handle
41
+ end
42
+
43
+ def md5_of_body
44
+ data.md5_of_body
45
+ end
46
+
47
+ def body
48
+ data.body
49
+ end
50
+
51
+ def attributes
52
+ data.attributes
53
+ end
54
+
55
+ def md5_of_message_attributes
56
+ data.md5_of_message_attributes
57
+ end
58
+
59
+ def message_attributes
60
+ data.message_attributes
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,61 +1,61 @@
1
- require 'toiler/aws/message'
2
-
3
- module Toiler
4
- module Aws
5
- # SQS Queue abstraction
6
- # Provides methods for querying and acting on a SQS queue
7
- class Queue
8
- attr_accessor :name, :client, :url
9
-
10
- def initialize(name, client = nil)
11
- @name = name
12
- @client = client || ::Aws::SQS::Client.new
13
- @url = client.get_queue_url(queue_name: name).queue_url
14
- end
15
-
16
- def visibility_timeout
17
- client.get_queue_attributes(
18
- queue_url: url,
19
- attribute_names: ['VisibilityTimeout']
20
- ).attributes['VisibilityTimeout'].to_i
21
- end
22
-
23
- def delete_messages(options)
24
- client.delete_message_batch options.merge queue_url: url
25
- end
26
-
27
- def send_message(options)
28
- client.send_message sanitize_message_body options.merge queue_url: url
29
- end
30
-
31
- def send_messages(options)
32
- client.send_message_batch(
33
- sanitize_message_body options.merge queue_url: url
34
- )
35
- end
36
-
37
- def receive_messages(options)
38
- client.receive_message(options.merge(queue_url: url))
39
- .messages
40
- .map { |m| Message.new(client, url, m) }
41
- end
42
-
43
- private
44
-
45
- def sanitize_message_body(options)
46
- messages = options[:entries] || [options]
47
-
48
- messages.each do |m|
49
- body = m[:message_body]
50
- if body.is_a?(Hash)
51
- m[:message_body] = JSON.dump(body)
52
- elsif !body.is_a? String
53
- fail ArgumentError, "Body must be a String, found #{body.class}"
54
- end
55
- end
56
-
57
- options
58
- end
59
- end
60
- end
61
- end
1
+ require 'toiler/aws/message'
2
+
3
+ module Toiler
4
+ module Aws
5
+ # SQS Queue abstraction
6
+ # Provides methods for querying and acting on a SQS queue
7
+ class Queue
8
+ attr_accessor :name, :client, :url
9
+
10
+ def initialize(name, client = nil)
11
+ @name = name
12
+ @client = client || ::Aws::SQS::Client.new
13
+ @url = client.get_queue_url(queue_name: name).queue_url
14
+ end
15
+
16
+ def visibility_timeout
17
+ client.get_queue_attributes(
18
+ queue_url: url,
19
+ attribute_names: ['VisibilityTimeout']
20
+ ).attributes['VisibilityTimeout'].to_i
21
+ end
22
+
23
+ def delete_messages(options)
24
+ client.delete_message_batch options.merge queue_url: url
25
+ end
26
+
27
+ def send_message(options)
28
+ client.send_message sanitize_message_body options.merge queue_url: url
29
+ end
30
+
31
+ def send_messages(options)
32
+ client.send_message_batch(
33
+ sanitize_message_body options.merge queue_url: url
34
+ )
35
+ end
36
+
37
+ def receive_messages(options)
38
+ client.receive_message(options.merge(queue_url: url))
39
+ .messages
40
+ .map { |m| Message.new(client, url, m) }
41
+ end
42
+
43
+ private
44
+
45
+ def sanitize_message_body(options)
46
+ messages = options[:entries] || [options]
47
+
48
+ messages.each do |m|
49
+ body = m[:message_body]
50
+ if body.is_a?(Hash)
51
+ m[:message_body] = JSON.dump(body)
52
+ elsif !body.is_a? String
53
+ fail ArgumentError, "Body must be a String, found #{body.class}"
54
+ end
55
+ end
56
+
57
+ options
58
+ end
59
+ end
60
+ end
61
+ end
data/lib/toiler/cli.rb CHANGED
@@ -1,153 +1,160 @@
1
- require 'singleton'
2
- require 'timeout'
3
- require 'optparse'
4
- require 'toiler'
5
-
6
- module Toiler
7
- # See: https://github.com/mperham/sidekiq/blob/33f5d6b2b6c0dfaab11e5d39688cab7ebadc83ae/lib/sidekiq/cli.rb#L20
8
- class Shutdown < Interrupt; end
9
-
10
- # Command line client interface
11
- class CLI
12
- include Singleton
13
-
14
- attr_accessor :supervisor
15
-
16
- def run(args)
17
- @self_read, @self_write = IO.pipe
18
-
19
- trap_signals
20
- options = Utils::ArgumentParser.parse(args)
21
- Utils::EnvironmentLoader.load(options)
22
- daemonize
23
- write_pid
24
- load_concurrent
25
- start_supervisor
26
-
27
- handle_stop
28
- end
29
-
30
- private
31
-
32
- def handle_stop
33
- while (readable_io = IO.select([@self_read]))
34
- handle_signal(readable_io.first[0].gets.strip)
35
- end
36
- rescue Interrupt
37
- puts 'Waiting up to 60 seconds for actors to finish...'
38
- supervisor.ask(:terminate!).wait(60)
39
- ensure
40
- exit 0
41
- end
42
-
43
- def shutdown_pools
44
- Concurrent.global_fast_executor.shutdown
45
- Concurrent.global_io_executor.shutdown
46
- return if Concurrent.global_io_executor.wait_for_termination(60)
47
- Concurrent.global_io_executor.kill
48
- end
49
-
50
- def start_supervisor
51
- require 'toiler/actor/supervisor'
52
- @supervisor = Actor::Supervisor.spawn! :supervisor
53
- end
54
-
55
- def trap_signals
56
- %w(INT TERM QUIT USR1 USR2 TTIN).each do |sig|
57
- begin
58
- trap sig do
59
- @self_write.puts(sig)
60
- end
61
- rescue ArgumentError
62
- puts "System does not support signal #{sig}"
63
- end
64
- end
65
- end
66
-
67
- def print_stacktraces
68
- return unless Toiler.logger
69
- Toiler.logger.info "-------------------\nReceived QUIT, dumping threads:"
70
- Thread.list.each do |t|
71
- id = t.object_id
72
- Toiler.logger.info "[thread:#{id}] #{t.backtrace.join("\n[thread:#{id}] ")}"
73
- end
74
- Toiler.logger.info '-------------------'
75
- end
76
-
77
- def print_status
78
- return unless Toiler.logger
79
- Toiler.logger.info "-------------------\nReceived QUIT, dumping status:"
80
- Toiler.queues.each do |queue|
81
- fetcher = Toiler.fetcher(queue).send(:core).send(:context)
82
- processor_pool = Toiler.processor_pool(queue).send(:core).send(:context)
83
- processors = processor_pool.instance_variable_get(:@workers).collect{|w| w.send(:core).send(:context)}
84
- busy_processors = processors.count{|pr| pr.executing?}
85
- Toiler.logger.info "[fetcher:#{fetcher.name}] [executing:#{fetcher.executing?}] [scheduled:#{fetcher.scheduled?}] [free_processors:#{fetcher.get_free_processors}]"
86
- Toiler.logger.info "[processor_pool:#{processor_pool.name}] [workers:#{processors.count}] [busy:#{busy_processors}]"
87
- processors.each do |processor|
88
- Toiler.logger.info "[processor:#{processor.name}] [executing:#{processor.executing?}] [thread:#{processor.thread.object_id}]"
89
- end
90
- end
91
- Toiler.logger.info '-------------------'
92
- end
93
-
94
- def handle_signal(signal)
95
- case signal
96
- when 'QUIT'
97
- print_stacktraces
98
- print_status
99
- when 'INT', 'TERM'
100
- fail Interrupt
101
- end
102
- end
103
-
104
- def load_concurrent
105
- fail 'Concurrent should not be required now' if defined?(::Concurrent)
106
- require 'concurrent-edge'
107
- Concurrent.global_logger = lambda do |level, progname, msg = nil, &block|
108
- Toiler.logger.log(level, msg, progname, &block)
109
- end if Toiler.logger
110
- end
111
-
112
- def daemonize
113
- return unless Toiler.options[:daemon]
114
- fail 'Logfile required when daemonizing' unless Toiler.options[:logfile]
115
-
116
- files_to_reopen = []
117
- ObjectSpace.each_object(File) do |file|
118
- files_to_reopen << file unless file.closed?
119
- end
120
-
121
- Process.daemon(true, true)
122
-
123
- reopen_files(files_to_reopen)
124
- reopen_std
125
- end
126
-
127
- def reopen_files(files_to_reopen)
128
- files_to_reopen.each do |file|
129
- begin
130
- file.reopen file.path, 'a+'
131
- file.sync = true
132
- rescue StandardError
133
- puts "Failed to reopen file #{file}"
134
- end
135
- end
136
- end
137
-
138
- def reopen_std
139
- [$stdout, $stderr].each do |io|
140
- File.open(Toiler.options[:logfile], 'ab') do |f|
141
- io.reopen(f)
142
- end
143
- io.sync = true
144
- end
145
- $stdin.reopen('/dev/null')
146
- end
147
-
148
- def write_pid
149
- file = Toiler.options[:pidfile]
150
- File.write file, Process.pid if file
151
- end
152
- end
153
- end
1
+ require 'singleton'
2
+ require 'timeout'
3
+ require 'optparse'
4
+ require 'toiler'
5
+
6
+ module Toiler
7
+ # See: https://github.com/mperham/sidekiq/blob/33f5d6b2b6c0dfaab11e5d39688cab7ebadc83ae/lib/sidekiq/cli.rb#L20
8
+ class Shutdown < Interrupt; end
9
+
10
+ # Command line client interface
11
+ class CLI
12
+ include Singleton
13
+
14
+ attr_accessor :supervisor
15
+
16
+ def run(args)
17
+ @self_read, @self_write = IO.pipe
18
+
19
+ trap_signals
20
+ options = Utils::ArgumentParser.parse(args)
21
+ Utils::EnvironmentLoader.load(options)
22
+ daemonize
23
+ write_pid
24
+ load_concurrent
25
+ start_supervisor
26
+
27
+ handle_stop
28
+ end
29
+
30
+ private
31
+
32
+ def handle_stop
33
+ while (readable_io = IO.select([@self_read]))
34
+ handle_signal(readable_io.first[0].gets.strip)
35
+ end
36
+ rescue Interrupt
37
+ puts 'Waiting up to 60 seconds for actors to finish...'
38
+ supervisor.ask(:terminate!).wait(60)
39
+ ensure
40
+ exit 0
41
+ end
42
+
43
+ def shutdown_pools
44
+ Concurrent.global_fast_executor.shutdown
45
+ Concurrent.global_io_executor.shutdown
46
+ return if Concurrent.global_io_executor.wait_for_termination(60)
47
+ Concurrent.global_io_executor.kill
48
+ end
49
+
50
+ def start_supervisor
51
+ require 'toiler/actor/supervisor'
52
+ @supervisor = Actor::Supervisor.spawn! :supervisor
53
+ end
54
+
55
+ def trap_signals
56
+ %w(INT TERM QUIT USR1 USR2 TTIN).each do |sig|
57
+ begin
58
+ trap sig do
59
+ @self_write.puts(sig)
60
+ end
61
+ rescue ArgumentError
62
+ puts "System does not support signal #{sig}"
63
+ end
64
+ end
65
+ end
66
+
67
+ def print_stacktraces
68
+ return unless Toiler.logger
69
+ Toiler.logger.info "-------------------"
70
+ Toiler.logger.info "Received QUIT, dumping threads:"
71
+ Thread.list.each do |t|
72
+ id = t.object_id
73
+ Toiler.logger.info "[thread:#{id}] #{t.backtrace.join("\n[thread:#{id}] ")}"
74
+ end
75
+ Toiler.logger.info '-------------------'
76
+ end
77
+
78
+ def print_status
79
+ return unless Toiler.logger
80
+ Toiler.logger.info "-------------------"
81
+ Toiler.logger.info "Received QUIT, dumping status:"
82
+ Toiler.queues.each do |queue|
83
+ fetcher = Toiler.fetcher(queue).send(:core).send(:context)
84
+ processor_pool = Toiler.processor_pool(queue).send(:core).send(:context)
85
+ processors = processor_pool.instance_variable_get(:@workers).collect{|w| w.send(:core).send(:context)}
86
+ busy_processors = processors.count{|pr| pr.executing?}
87
+ message = "Status for [queue:#{queue}]:"
88
+ message += "\n[fetcher:#{fetcher.name}] [executing:#{fetcher.executing?}] [polling:#{fetcher.polling?}] [scheduled:#{fetcher.scheduled?}] [free_processors:#{fetcher.get_free_processors}]"
89
+ message += "\n[processor_pool:#{processor_pool.name}] [workers:#{processors.count}] [busy:#{busy_processors}]"
90
+ processors.each do |processor|
91
+ thread = processor.thread
92
+ thread_id = thread.nil? ? nil : thread.object_id
93
+ message += "\n[processor:#{processor.name}] [executing:#{processor.executing?}] [thread:#{thread_id}] Stack:"
94
+ message += thread.backtrace.join("\n\t") unless thread.nil?
95
+ end
96
+ Toiler.logger.info message
97
+ end
98
+ Toiler.logger.info '-------------------'
99
+ end
100
+
101
+ def handle_signal(signal)
102
+ case signal
103
+ when 'QUIT'
104
+ print_stacktraces
105
+ print_status
106
+ when 'INT', 'TERM'
107
+ fail Interrupt
108
+ end
109
+ end
110
+
111
+ def load_concurrent
112
+ fail 'Concurrent should not be required now' if defined?(::Concurrent)
113
+ require 'concurrent-edge'
114
+ Concurrent.global_logger = lambda do |level, progname, msg = nil, &block|
115
+ Toiler.logger.log(level, msg, progname, &block)
116
+ end if Toiler.logger
117
+ end
118
+
119
+ def daemonize
120
+ return unless Toiler.options[:daemon]
121
+ fail 'Logfile required when daemonizing' unless Toiler.options[:logfile]
122
+
123
+ files_to_reopen = []
124
+ ObjectSpace.each_object(File) do |file|
125
+ files_to_reopen << file unless file.closed?
126
+ end
127
+
128
+ Process.daemon(true, true)
129
+
130
+ reopen_files(files_to_reopen)
131
+ reopen_std
132
+ end
133
+
134
+ def reopen_files(files_to_reopen)
135
+ files_to_reopen.each do |file|
136
+ begin
137
+ file.reopen file.path, 'a+'
138
+ file.sync = true
139
+ rescue StandardError
140
+ puts "Failed to reopen file #{file}"
141
+ end
142
+ end
143
+ end
144
+
145
+ def reopen_std
146
+ [$stdout, $stderr].each do |io|
147
+ File.open(Toiler.options[:logfile], 'ab') do |f|
148
+ io.reopen(f)
149
+ end
150
+ io.sync = true
151
+ end
152
+ $stdin.reopen('/dev/null')
153
+ end
154
+
155
+ def write_pid
156
+ file = Toiler.options[:pidfile]
157
+ File.write file, Process.pid if file
158
+ end
159
+ end
160
+ end
@@ -1,50 +1,50 @@
1
- module Toiler
2
- module Utils
3
- # Parses command-line arguments
4
- module ArgumentParser
5
- module_function
6
-
7
- def parse(argv)
8
- opts = { queues: [] }
9
-
10
- parser = OptionParser.new do |o|
11
- o.on '-d', '--daemon', 'Daemonize process' do |arg|
12
- opts[:daemon] = arg
13
- end
14
-
15
- o.on '-r', '--require [PATH|DIR]', 'Location of the worker' do |arg|
16
- opts[:require] = arg
17
- end
18
-
19
- o.on '-C', '--config PATH', 'Path to YAML config file' do |arg|
20
- opts[:config_file] = arg
21
- end
22
-
23
- o.on '-R', '--rails', 'Load Rails' do |arg|
24
- opts[:rails] = arg
25
- end
26
-
27
- o.on '-L', '--logfile PATH', 'Path to writable logfile' do |arg|
28
- opts[:logfile] = arg
29
- end
30
-
31
- o.on '-P', '--pidfile PATH', 'Path to pidfile' do |arg|
32
- opts[:pidfile] = arg
33
- end
34
-
35
- o.on '-v', '--verbose', 'Print more verbose output' do |arg|
36
- opts[:verbose] = arg
37
- end
38
- end
39
-
40
- parser.banner = 'toiler [options]'
41
- parser.on_tail '-h', '--help', 'Show help' do
42
- puts parser
43
- exit 1
44
- end
45
- parser.parse!(argv)
46
- opts
47
- end
48
- end
49
- end
50
- end
1
+ module Toiler
2
+ module Utils
3
+ # Parses command-line arguments
4
+ module ArgumentParser
5
+ module_function
6
+
7
+ def parse(argv)
8
+ opts = { queues: [] }
9
+
10
+ parser = OptionParser.new do |o|
11
+ o.on '-d', '--daemon', 'Daemonize process' do |arg|
12
+ opts[:daemon] = arg
13
+ end
14
+
15
+ o.on '-r', '--require [PATH|DIR]', 'Location of the worker' do |arg|
16
+ opts[:require] = arg
17
+ end
18
+
19
+ o.on '-C', '--config PATH', 'Path to YAML config file' do |arg|
20
+ opts[:config_file] = arg
21
+ end
22
+
23
+ o.on '-R', '--rails', 'Load Rails' do |arg|
24
+ opts[:rails] = arg
25
+ end
26
+
27
+ o.on '-L', '--logfile PATH', 'Path to writable logfile' do |arg|
28
+ opts[:logfile] = arg
29
+ end
30
+
31
+ o.on '-P', '--pidfile PATH', 'Path to pidfile' do |arg|
32
+ opts[:pidfile] = arg
33
+ end
34
+
35
+ o.on '-v', '--verbose', 'Print more verbose output' do |arg|
36
+ opts[:verbose] = arg
37
+ end
38
+ end
39
+
40
+ parser.banner = 'toiler [options]'
41
+ parser.on_tail '-h', '--help', 'Show help' do
42
+ puts parser
43
+ exit 1
44
+ end
45
+ parser.parse!(argv)
46
+ opts
47
+ end
48
+ end
49
+ end
50
+ end