toiler 0.3.1.beta1 → 0.3.1.beta2

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