qwirk 0.0.1
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.
- data/History.md +7 -0
- data/LICENSE.txt +201 -0
- data/README.md +180 -0
- data/Rakefile +34 -0
- data/examples/README +1 -0
- data/examples/activemq.xml +84 -0
- data/examples/advanced_requestor/README.md +15 -0
- data/examples/advanced_requestor/base_request_worker.rb +18 -0
- data/examples/advanced_requestor/char_count_worker.rb +16 -0
- data/examples/advanced_requestor/config.ru +24 -0
- data/examples/advanced_requestor/exception_raiser_worker.rb +17 -0
- data/examples/advanced_requestor/length_worker.rb +14 -0
- data/examples/advanced_requestor/print_worker.rb +14 -0
- data/examples/advanced_requestor/publisher.rb +49 -0
- data/examples/advanced_requestor/qwirk.yml +16 -0
- data/examples/advanced_requestor/reverse_worker.rb +14 -0
- data/examples/advanced_requestor/triple_worker.rb +14 -0
- data/examples/batch/my_batch_worker.rb +30 -0
- data/examples/batch/my_line_worker.rb +8 -0
- data/examples/qwirk.yml +20 -0
- data/examples/requestor/README.md +13 -0
- data/examples/requestor/config.ru +13 -0
- data/examples/requestor/qwirk_persist.yml +5 -0
- data/examples/requestor/requestor.rb +68 -0
- data/examples/requestor/reverse_echo_worker.rb +15 -0
- data/examples/setup.rb +13 -0
- data/examples/shared/README.md +24 -0
- data/examples/shared/config.ru +13 -0
- data/examples/shared/publisher.rb +49 -0
- data/examples/shared/qwirk_persist.yml +5 -0
- data/examples/shared/shared_worker.rb +16 -0
- data/examples/simple/README +53 -0
- data/examples/simple/bar_worker.rb +10 -0
- data/examples/simple/baz_worker.rb +10 -0
- data/examples/simple/config.ru +14 -0
- data/examples/simple/publisher.rb +49 -0
- data/examples/simple/qwirk_persist.yml +4 -0
- data/examples/simple/tmp/kahadb/db-1.log +0 -0
- data/examples/simple/tmp/kahadb/db.data +0 -0
- data/examples/simple/tmp/kahadb/db.redo +0 -0
- data/examples/task/README +47 -0
- data/examples/task/config.ru +14 -0
- data/examples/task/foo_worker.rb +10 -0
- data/examples/task/messages.out +1000 -0
- data/examples/task/publisher.rb +25 -0
- data/examples/task/qwirk_persist.yml +5 -0
- data/examples/task/task.rb +36 -0
- data/lib/qwirk.rb +63 -0
- data/lib/qwirk/adapter.rb +45 -0
- data/lib/qwirk/base_worker.rb +96 -0
- data/lib/qwirk/batch.rb +4 -0
- data/lib/qwirk/batch/acquire_file_strategy.rb +47 -0
- data/lib/qwirk/batch/active_record.rb +3 -0
- data/lib/qwirk/batch/active_record/batch_job.rb +111 -0
- data/lib/qwirk/batch/active_record/failed_record.rb +5 -0
- data/lib/qwirk/batch/active_record/outstanding_record.rb +6 -0
- data/lib/qwirk/batch/file_status_strategy.rb +86 -0
- data/lib/qwirk/batch/file_worker.rb +228 -0
- data/lib/qwirk/batch/job_status.rb +29 -0
- data/lib/qwirk/batch/parse_file_strategy.rb +48 -0
- data/lib/qwirk/engine.rb +9 -0
- data/lib/qwirk/loggable.rb +23 -0
- data/lib/qwirk/manager.rb +140 -0
- data/lib/qwirk/marshal_strategy.rb +74 -0
- data/lib/qwirk/marshal_strategy/bson.rb +37 -0
- data/lib/qwirk/marshal_strategy/json.rb +37 -0
- data/lib/qwirk/marshal_strategy/none.rb +26 -0
- data/lib/qwirk/marshal_strategy/ruby.rb +26 -0
- data/lib/qwirk/marshal_strategy/string.rb +25 -0
- data/lib/qwirk/marshal_strategy/yaml.rb +25 -0
- data/lib/qwirk/publish_handle.rb +170 -0
- data/lib/qwirk/publisher.rb +67 -0
- data/lib/qwirk/queue_adapter.rb +3 -0
- data/lib/qwirk/queue_adapter/active_mq.rb +13 -0
- data/lib/qwirk/queue_adapter/active_mq/publisher.rb +12 -0
- data/lib/qwirk/queue_adapter/active_mq/worker_config.rb +16 -0
- data/lib/qwirk/queue_adapter/in_mem.rb +7 -0
- data/lib/qwirk/queue_adapter/in_mem/factory.rb +45 -0
- data/lib/qwirk/queue_adapter/in_mem/publisher.rb +98 -0
- data/lib/qwirk/queue_adapter/in_mem/queue.rb +88 -0
- data/lib/qwirk/queue_adapter/in_mem/reply_queue.rb +56 -0
- data/lib/qwirk/queue_adapter/in_mem/topic.rb +48 -0
- data/lib/qwirk/queue_adapter/in_mem/worker.rb +63 -0
- data/lib/qwirk/queue_adapter/in_mem/worker_config.rb +59 -0
- data/lib/qwirk/queue_adapter/jms.rb +50 -0
- data/lib/qwirk/queue_adapter/jms/connection.rb +42 -0
- data/lib/qwirk/queue_adapter/jms/consumer.rb +37 -0
- data/lib/qwirk/queue_adapter/jms/publisher.rb +126 -0
- data/lib/qwirk/queue_adapter/jms/worker.rb +89 -0
- data/lib/qwirk/queue_adapter/jms/worker_config.rb +38 -0
- data/lib/qwirk/remote_exception.rb +42 -0
- data/lib/qwirk/request_worker.rb +62 -0
- data/lib/qwirk/task.rb +177 -0
- data/lib/qwirk/task.rb.sav +194 -0
- data/lib/qwirk/version.rb +3 -0
- data/lib/qwirk/worker.rb +222 -0
- data/lib/qwirk/worker_config.rb +187 -0
- data/lib/rails/generators/qwirk/qwirk_generator.rb +82 -0
- data/lib/rails/generators/qwirk/templates/initializer.rb +6 -0
- data/lib/rails/generators/qwirk/templates/migration.rb +9 -0
- data/lib/rails/generators/qwirk/templates/schema.rb +28 -0
- data/lib/rails/railties/tasks.rake +8 -0
- data/lib/tasks/qwirk_tasks.rake +4 -0
- data/test/base_test.rb +248 -0
- data/test/database.yml +14 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +45 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +22 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +26 -0
- data/test/dummy/config/environments/production.rb +49 -0
- data/test/dummy/config/environments/test.rb +35 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/log/development.log +0 -0
- data/test/dummy/log/production.log +0 -0
- data/test/dummy/log/server.log +0 -0
- data/test/dummy/log/test.log +0 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +2 -0
- data/test/dummy/public/javascripts/controls.js +965 -0
- data/test/dummy/public/javascripts/dragdrop.js +974 -0
- data/test/dummy/public/javascripts/effects.js +1123 -0
- data/test/dummy/public/javascripts/prototype.js +6001 -0
- data/test/dummy/public/javascripts/rails.js +191 -0
- data/test/dummy/script/rails +6 -0
- data/test/integration/navigation_test.rb +7 -0
- data/test/jms.yml +9 -0
- data/test/jms_fail_test.rb +149 -0
- data/test/jms_requestor_block_test.rb +278 -0
- data/test/jms_requestor_test.rb +238 -0
- data/test/jms_test.rb +287 -0
- data/test/marshal_strategy_test.rb +62 -0
- data/test/support/integration_case.rb +5 -0
- data/test/test_helper.rb +7 -0
- data/test/test_helper.rbold +22 -0
- data/test/test_helper_active_record.rb +61 -0
- data/test/unit/qwirk/batch/acquire_file_strategy_test.rb +101 -0
- data/test/unit/qwirk/batch/active_record/batch_job_test.rb +35 -0
- data/test/unit/qwirk/batch/parse_file_strategy_test.rb +49 -0
- metadata +366 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
class RemoteException < Exception
|
|
3
|
+
attr_accessor :originating_exception_name
|
|
4
|
+
attr_accessor :originating_exception_message
|
|
5
|
+
|
|
6
|
+
def initialize(originating_exception=nil, message=nil)
|
|
7
|
+
super(message)
|
|
8
|
+
if originating_exception
|
|
9
|
+
@originating_exception_name = originating_exception.class.name
|
|
10
|
+
@originating_exception_message = originating_exception.message
|
|
11
|
+
set_backtrace(originating_exception.backtrace)
|
|
12
|
+
end
|
|
13
|
+
@message = message
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def message
|
|
17
|
+
@message || @originating_exception_message
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def message=(msg)
|
|
21
|
+
@message = msg
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_hash
|
|
25
|
+
{
|
|
26
|
+
'message' => @message,
|
|
27
|
+
'originating_exception_name' => @originating_exception_name,
|
|
28
|
+
'originating_exception_message' => @originating_exception_message,
|
|
29
|
+
'backtrace' => backtrace
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.from_hash(hash)
|
|
34
|
+
exc = new
|
|
35
|
+
exc.message = hash['message']
|
|
36
|
+
exc.originating_exception_name = hash['originating_exception_name']
|
|
37
|
+
exc.originating_exception_message = hash['originating_exception_message']
|
|
38
|
+
exc.set_backtrace(hash['backtrace'])
|
|
39
|
+
return exc
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
|
|
3
|
+
# Base Worker Class for any class that will be processing requests from queues and replying
|
|
4
|
+
module RequestWorker
|
|
5
|
+
include Worker
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
# Define the marshaling and time_to_live that will occur on the response
|
|
9
|
+
def response(options)
|
|
10
|
+
queue_options[:response] = options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# By default, exceptions don't get forwarded to a fail queue (they get returned to the caller)
|
|
14
|
+
def default_fail_queue_target
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.included(base)
|
|
20
|
+
Worker.included(base)
|
|
21
|
+
base.extend(ClassMethods)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def perform(object)
|
|
25
|
+
begin
|
|
26
|
+
response = request(object)
|
|
27
|
+
rescue Exception => e
|
|
28
|
+
on_exception(e)
|
|
29
|
+
else
|
|
30
|
+
adapter.send_response(message, config.marshaler.marshal(response))
|
|
31
|
+
end
|
|
32
|
+
post_request(object)
|
|
33
|
+
rescue Exception => e
|
|
34
|
+
Qwirk.logger.error("Exception in send_response or post_request: #{e.message}")
|
|
35
|
+
log_backtrace(e)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def request(object)
|
|
39
|
+
raise "#{self}: Need to override request method in #{self.class.name} in order to act on #{object}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Handle any processing that you want to perform after the reply
|
|
43
|
+
def post_request(object)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
#########
|
|
47
|
+
protected
|
|
48
|
+
#########
|
|
49
|
+
|
|
50
|
+
def on_exception(e)
|
|
51
|
+
begin
|
|
52
|
+
adapter.send_exception(message, e)
|
|
53
|
+
rescue Exception => e
|
|
54
|
+
Qwirk.logger.error("Exception in exception reply: #{e.message}")
|
|
55
|
+
log_backtrace(e)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Send it on to the fail queue if it was explicitly set (See default_fail_queue_target above)
|
|
59
|
+
super
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/qwirk/task.rb
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
|
|
3
|
+
# The following options can be used for configuring the class
|
|
4
|
+
# :
|
|
5
|
+
module Task
|
|
6
|
+
#include Qwirk::BaseWorker
|
|
7
|
+
include Rumx::Bean
|
|
8
|
+
|
|
9
|
+
bean_attr_reader :task_id, :string, 'The ID for this task'
|
|
10
|
+
bean_attr_reader :publish_count, :integer, 'The number of requests that have been published'
|
|
11
|
+
bean_attr_reader :success_count, :integer, 'The number of successful responses'
|
|
12
|
+
bean_attr_reader :exception_count, :integer, 'The number of exception responses'
|
|
13
|
+
bean_attr_reader :total_count, :integer, 'The total expected records to be published (optional)'
|
|
14
|
+
bean_attr_accessor :retry, :boolean, 'Retry all the exception responses'
|
|
15
|
+
bean_attr_accessor :auto_retry, :boolean, 'Continuously retry all the exception responses while at least 1 or more succeeds'
|
|
16
|
+
bean_attr_reader :exceptions_per_run, :list, 'Number of exceptions per run, i.e., index 0 contains the count of exceptions in the first run, index 1 in the first retry, etc.', :list_type => :integer
|
|
17
|
+
|
|
18
|
+
module ClassMethods
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.included(base)
|
|
22
|
+
#Qwirk::BaseWorker.included(base)
|
|
23
|
+
Rumx::Bean.included(base)
|
|
24
|
+
base.extend(ClassMethods)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(publisher, task_id, total_count, opts={})
|
|
28
|
+
@publisher = publisher
|
|
29
|
+
@mutex = Mutex.new
|
|
30
|
+
@condition = ConditionVariable.new
|
|
31
|
+
@task_id = task_id
|
|
32
|
+
@stopped = false
|
|
33
|
+
@finished_publishing = false
|
|
34
|
+
@retry = opts[:retry]
|
|
35
|
+
@auto_retry = opts[:auto_retry]
|
|
36
|
+
@publish_count = 0
|
|
37
|
+
@success_count = 0
|
|
38
|
+
@exception_count = 0
|
|
39
|
+
@total_count = total_count
|
|
40
|
+
@exceptions_per_run = []
|
|
41
|
+
|
|
42
|
+
@producer, @consumer = publisher.create_producer_consumer_pair(self)
|
|
43
|
+
@reply_thread = Thread.new do
|
|
44
|
+
java.lang.Thread.current_thread.name = "Qwirk task: #{task_id}"
|
|
45
|
+
reply_event_loop
|
|
46
|
+
on_done
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Stuff to override
|
|
51
|
+
def on_response(response)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def on_exception(exception)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def on_update()
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def on_done()
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def retry=(val)
|
|
64
|
+
@retry = val
|
|
65
|
+
if val
|
|
66
|
+
@mutex.synchronize { check_retry }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def auto_retry=(val)
|
|
71
|
+
@auto_retry = val
|
|
72
|
+
if val
|
|
73
|
+
@mutex.synchronize { check_retry }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def publish(object)
|
|
78
|
+
marshaled_object = @publisher.marshaler.marshal(object)
|
|
79
|
+
@mutex.synchronize do
|
|
80
|
+
unless @stopped
|
|
81
|
+
@producer.send(marshaled_object)
|
|
82
|
+
@publish_count += 1
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# TODO: Needed?
|
|
88
|
+
def start
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def stop
|
|
92
|
+
@mutex.synchronize { do_stop }
|
|
93
|
+
@reply_thread.join
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def finished_publishing
|
|
97
|
+
@total_count = @publish_count
|
|
98
|
+
@finished_publishing = true
|
|
99
|
+
@mutex.synchronize { check_finish }
|
|
100
|
+
@reply_thread.join
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#######
|
|
104
|
+
private
|
|
105
|
+
#######
|
|
106
|
+
|
|
107
|
+
# Must be called within a mutex synchronize
|
|
108
|
+
def do_stop
|
|
109
|
+
return if @stopped
|
|
110
|
+
@consumer.stop if @consumer
|
|
111
|
+
@fail_consumer.stop if @fail_consumer
|
|
112
|
+
@stopped = true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def reply_event_loop
|
|
116
|
+
while !@stopped && response = @consumer.receive
|
|
117
|
+
@mutex.synchronize do
|
|
118
|
+
unless @stopped
|
|
119
|
+
if response.kind_of?(RemoteException)
|
|
120
|
+
@exception_count += 1
|
|
121
|
+
on_exception(response)
|
|
122
|
+
else
|
|
123
|
+
@success_count += 1
|
|
124
|
+
on_response(response)
|
|
125
|
+
end
|
|
126
|
+
@consumer.acknowledge_message
|
|
127
|
+
check_finish
|
|
128
|
+
@condition.signal
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
do_stop
|
|
133
|
+
Qwirk.logger.info "#{self}: Exiting"
|
|
134
|
+
rescue Exception => e
|
|
135
|
+
do_stop
|
|
136
|
+
Qwirk.logger.error "#{self}: Exception, thread terminating: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Must be called within a mutex synchronize
|
|
140
|
+
def check_finish
|
|
141
|
+
if @finished_publishing
|
|
142
|
+
if @success_count >= @total_count
|
|
143
|
+
do_stop
|
|
144
|
+
else
|
|
145
|
+
check_retry
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Must be called within a mutex synchronize
|
|
151
|
+
def check_retry
|
|
152
|
+
if @finished_publishing && @exception_count > 0 && (@exception_count+@success_count) == @total_count && (@retry || @auto_retry)
|
|
153
|
+
# If we're just doing auto_retry but nothing succeeded last time, then don't run again
|
|
154
|
+
return if !@retry && @auto_retry && @exception_count == @exceptions_per_run.last
|
|
155
|
+
Qwirk.logger.info "#{self}: Retrying exception records, exception count = #{@exception_count}"
|
|
156
|
+
@exceptions_per_run << @exception_count
|
|
157
|
+
@exception_count = 0
|
|
158
|
+
@finished_publishing = false
|
|
159
|
+
@fail_thread = Thread.new(@exceptions_per_run.last) do |count|
|
|
160
|
+
begin
|
|
161
|
+
java.lang.Thread.current_thread.name = "Qwirk fail task: #{task_id}"
|
|
162
|
+
while !@stopped && (count > 0) && (object = @fail_consumer.receive)
|
|
163
|
+
count -= 1
|
|
164
|
+
publish(object)
|
|
165
|
+
@fail_consumer.acknowledge_message
|
|
166
|
+
end
|
|
167
|
+
@finished_publishing = true
|
|
168
|
+
@mutex.synchronize { check_finish }
|
|
169
|
+
rescue Exception => e
|
|
170
|
+
do_stop
|
|
171
|
+
Qwirk.logger.error "#{self}: Exception, thread terminating: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
|
|
3
|
+
# The following options can be used for configuring the class
|
|
4
|
+
# :max_pending_records => <integer>
|
|
5
|
+
# This is how many records can be queued at a time.
|
|
6
|
+
# :
|
|
7
|
+
module Task
|
|
8
|
+
#include Qwirk::BaseWorker
|
|
9
|
+
include Rumx::Bean
|
|
10
|
+
|
|
11
|
+
bean_attr_accessor :max_pending_records, :integer, 'The max number of records that can be published without having been responded to (publishing blocks at this point).'
|
|
12
|
+
bean_attr_reader :task_id, :string, 'The ID for this task'
|
|
13
|
+
bean_attr_reader :success_count, :integer, 'The number of successful responses'
|
|
14
|
+
bean_attr_reader :exception_count, :integer, 'The number of exception responses'
|
|
15
|
+
bean_attr_reader :total_count, :integer, 'The total expected records to be published (optional)'
|
|
16
|
+
bean_attr_accessor :retry, :boolean, 'Retry all the exception responses'
|
|
17
|
+
bean_attr_accessor :auto_retry, :boolean, 'Continuously retry all the exception responses while at least 1 or more succeeds'
|
|
18
|
+
bean_attr_reader :exceptions_per_run, :list, 'Number of exceptions per run, i.e., index 0 contains the count of exceptions in the first run, index 1 in the first retry, etc.', :list_type => :integer
|
|
19
|
+
|
|
20
|
+
module ClassMethods
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.included(base)
|
|
24
|
+
#Qwirk::BaseWorker.included(base)
|
|
25
|
+
Rumx::Bean.included(base)
|
|
26
|
+
base.extend(ClassMethods)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(publisher, task_id, total_count, opts={})
|
|
30
|
+
@publisher = publisher
|
|
31
|
+
@pending_hash = Hash.new
|
|
32
|
+
@pending_hash_mutex = Mutex.new
|
|
33
|
+
@pending_hash_condition = ConditionVariable.new
|
|
34
|
+
@task_id = task_id
|
|
35
|
+
@stopped = false
|
|
36
|
+
@finished_publishing = false
|
|
37
|
+
@max_pending_records = opts[:max_pending_records] || 100
|
|
38
|
+
@retry = opts[:retry]
|
|
39
|
+
@auto_retry = opts[:auto_retry]
|
|
40
|
+
@success_count = 0
|
|
41
|
+
@exception_count = 0
|
|
42
|
+
@total_count = total_count
|
|
43
|
+
@exceptions_per_run = []
|
|
44
|
+
|
|
45
|
+
@producer, @consumer = publisher.create_producer_consumer_pair(self)
|
|
46
|
+
@reply_thread = Thread.new do
|
|
47
|
+
java.lang.Thread.current_thread.name = "Qwirk task: #{task_id}"
|
|
48
|
+
reply_event_loop
|
|
49
|
+
on_done
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Stuff to override
|
|
54
|
+
def on_response(request, response)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def on_exception(request, exception)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def on_update()
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def on_done()
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def retry=(val)
|
|
67
|
+
@retry = val
|
|
68
|
+
if val
|
|
69
|
+
@pending_hash_mutex.synchronize { check_retry }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def publish(object)
|
|
74
|
+
marshaled_object = @publisher.marshaler.marshal(object)
|
|
75
|
+
@pending_hash_mutex.synchronize do
|
|
76
|
+
while !@stopped && @pending_hash.size >= @max_pending_records
|
|
77
|
+
@pending_hash_condition.wait(@pending_hash_mutex)
|
|
78
|
+
end
|
|
79
|
+
unless @stopped
|
|
80
|
+
message_id = @producer.send(marshaled_object)
|
|
81
|
+
@pending_hash[message_id] = object
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# TODO: Needed?
|
|
87
|
+
def start
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def stop
|
|
91
|
+
@pending_hash_mutex.synchronize { do_stop }
|
|
92
|
+
@reply_thread.join
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def finished_publishing
|
|
96
|
+
@finished_publishing = true
|
|
97
|
+
@pending_hash_mutex.synchronize { check_finish }
|
|
98
|
+
@reply_thread.join
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
#######
|
|
102
|
+
private
|
|
103
|
+
#######
|
|
104
|
+
|
|
105
|
+
def verify_fail_queue_creation
|
|
106
|
+
unless @fail_producer
|
|
107
|
+
@fail_producer, @fail_consumer = publisher.create_producer_fail_consumer_pair(@task_id)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def publish_fail_request(object)
|
|
112
|
+
verify_fail_queue_creation
|
|
113
|
+
marshaled_object = @publisher.marshaler.marshal(object)
|
|
114
|
+
@fail_producer.send(marshaled_object)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Must be called within a mutex synchronize
|
|
118
|
+
def do_stop
|
|
119
|
+
return if @stopped
|
|
120
|
+
@consumer.stop if @consumer
|
|
121
|
+
@fail_consumer.stop if @fail_consumer
|
|
122
|
+
@stopped = true
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def reply_event_loop
|
|
126
|
+
while !@stopped && pair = @consumer.receive
|
|
127
|
+
message_id, response = pair
|
|
128
|
+
@pending_hash_mutex.synchronize do
|
|
129
|
+
unless @stopped
|
|
130
|
+
request = @pending_hash.delete(message_id)
|
|
131
|
+
if request
|
|
132
|
+
if response.kind_of?(RemoteException)
|
|
133
|
+
publish_fail_request(request)
|
|
134
|
+
on_exception(request, response)
|
|
135
|
+
@exception_count += 1
|
|
136
|
+
else
|
|
137
|
+
on_response(request, response)
|
|
138
|
+
@success_count += 1
|
|
139
|
+
end
|
|
140
|
+
else
|
|
141
|
+
Qwirk.logger.warn("#{self}: Read unexpected response with message_id=#{message_id}")
|
|
142
|
+
end
|
|
143
|
+
@consumer.acknowledge_message
|
|
144
|
+
check_finish
|
|
145
|
+
@pending_hash_condition.signal
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
do_stop
|
|
150
|
+
Qwirk.logger.info "#{self}: Exiting"
|
|
151
|
+
rescue Exception => e
|
|
152
|
+
do_stop
|
|
153
|
+
Qwirk.logger.error "#{self}: Exception, thread terminating: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Must be called within a mutex synchronize
|
|
157
|
+
def check_finish
|
|
158
|
+
if @finished_publishing && @pending_hash.empty?
|
|
159
|
+
if @exception_count == 0
|
|
160
|
+
do_stop
|
|
161
|
+
else
|
|
162
|
+
check_retry
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Must be called within a mutex synchronize
|
|
168
|
+
def check_retry
|
|
169
|
+
if @finished_publishing && @pending_hash.empty? && @exception_count > 0 && (@retry || @auto_retry)
|
|
170
|
+
# If we're just doing auto_retry but nothing succeeded last time, then don't run again
|
|
171
|
+
return if !@retry && @auto_retry && @exception_count == @exceptions_per_run.last
|
|
172
|
+
Qwirk.logger.info "#{self}: Retrying exception records, exception count = #{@exception_count}"
|
|
173
|
+
@exceptions_per_run << @exception_count
|
|
174
|
+
@exception_count = 0
|
|
175
|
+
@finished_publishing = false
|
|
176
|
+
@fail_thread = Thread.new(@exceptions_per_run.last) do |count|
|
|
177
|
+
begin
|
|
178
|
+
java.lang.Thread.current_thread.name = "Qwirk fail task: #{task_id}"
|
|
179
|
+
while !@stopped && (count > 0) && (object = @fail_consumer.receive)
|
|
180
|
+
count -= 1
|
|
181
|
+
publish(object)
|
|
182
|
+
@fail_consumer.acknowledge_message
|
|
183
|
+
end
|
|
184
|
+
@finished_publishing = true
|
|
185
|
+
@pending_hash_mutex.synchronize { check_finish }
|
|
186
|
+
rescue Exception => e
|
|
187
|
+
do_stop
|
|
188
|
+
Qwirk.logger.error "#{self}: Exception, thread terminating: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|