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,25 @@
|
|
|
1
|
+
require 'rumx'
|
|
2
|
+
require 'qwirk'
|
|
3
|
+
|
|
4
|
+
class Publisher
|
|
5
|
+
include Rumx::Bean
|
|
6
|
+
|
|
7
|
+
bean_attr_reader :tasks, :list, 'Tasks', :list_type => :bean
|
|
8
|
+
|
|
9
|
+
bean_operation :perform_task, :void, 'Perform task which will send <count> messages to the worker and write the output to <output_file>', [
|
|
10
|
+
[ :task_id, :string, 'Id for this task ', 'task1' ],
|
|
11
|
+
[ :count, :integer, 'Count of messages', 1000 ],
|
|
12
|
+
[ :message, :string, 'String portion of the message to send', 'M' ],
|
|
13
|
+
[ :sleep_time, :float, 'Time to sleep between messages', 0.0 ],
|
|
14
|
+
[ :output_file, :string, 'Output file to write returned messages to', 'messages.out' ]
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def initialize(adapter_key)
|
|
18
|
+
@adapter_key = adapter_key
|
|
19
|
+
@tasks = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def perform_task(task_id, count, message, sleep_time, output_file)
|
|
23
|
+
@tasks << Task.new(@adapter_key, task_id, count, message, sleep_time, output_file)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'rumx'
|
|
2
|
+
require 'qwirk'
|
|
3
|
+
|
|
4
|
+
class Task
|
|
5
|
+
include Qwirk::Task
|
|
6
|
+
|
|
7
|
+
def initialize(adapter_key, task_id, total_count, message, sleep_time, output_file)
|
|
8
|
+
publisher = Qwirk[adapter_key].create_publisher(:queue_name => 'Foo', :marshal => :bson, :persistent => false, :response => {:time_to_live => 10000, :marshal => :string})
|
|
9
|
+
super(publisher, task_id, total_count)
|
|
10
|
+
@out = File.open(output_file, 'w')
|
|
11
|
+
(1..total_count).each do |i|
|
|
12
|
+
obj = {'count' => i, 'message' => message}
|
|
13
|
+
#puts "Publishing object: #{obj.inspect}"
|
|
14
|
+
publish(obj)
|
|
15
|
+
sleep sleep_time
|
|
16
|
+
end
|
|
17
|
+
finished_publishing
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def on_response(request, response)
|
|
21
|
+
#puts "For request #{request}, got response #{response}"
|
|
22
|
+
@out.puts response
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def on_exception(request, exception)
|
|
26
|
+
puts "For request #{request} got exception #{exception.message}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def on_update()
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def on_done
|
|
33
|
+
@out.close
|
|
34
|
+
puts "We're done"
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/qwirk.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'qwirk/remote_exception'
|
|
3
|
+
require 'qwirk/marshal_strategy'
|
|
4
|
+
require 'qwirk/base_worker'
|
|
5
|
+
require 'qwirk/worker_config'
|
|
6
|
+
require 'qwirk/worker'
|
|
7
|
+
require 'qwirk/request_worker'
|
|
8
|
+
require 'qwirk/task'
|
|
9
|
+
require 'qwirk/publisher'
|
|
10
|
+
require 'qwirk/publish_handle'
|
|
11
|
+
require 'qwirk/adapter'
|
|
12
|
+
require 'qwirk/queue_adapter'
|
|
13
|
+
#require 'qwirk/batch'
|
|
14
|
+
require 'qwirk/manager'
|
|
15
|
+
require 'qwirk/loggable'
|
|
16
|
+
|
|
17
|
+
module Qwirk
|
|
18
|
+
extend Qwirk::Loggable
|
|
19
|
+
|
|
20
|
+
DEFAULT_NAME = 'Qwirk'
|
|
21
|
+
|
|
22
|
+
@@config = nil
|
|
23
|
+
@@hash = {}
|
|
24
|
+
|
|
25
|
+
class MyBean
|
|
26
|
+
include Rumx::Bean
|
|
27
|
+
|
|
28
|
+
bean_attr_reader :adapters, :hash, 'Adapters', :hash_type => :bean
|
|
29
|
+
|
|
30
|
+
def initialize(hash)
|
|
31
|
+
@adapters = hash
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.config=(config)
|
|
36
|
+
@@config = config
|
|
37
|
+
Rumx::Bean.root.bean_add_child(DEFAULT_NAME, MyBean.new(@@hash))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.[](key)
|
|
41
|
+
if @@config.nil?
|
|
42
|
+
if defined?(Rails)
|
|
43
|
+
# Allow user to use JMS w/o modifying qwirk.yml which could be checked in and hose other users
|
|
44
|
+
env = ENV['QWIRK_ENV'] || Rails.env
|
|
45
|
+
self.config = YAML.load(ERB.new(File.read(Rails.root.join("config", "qwirk.yml")), nil, '-').result(binding))[env]
|
|
46
|
+
Manager.default_options = {
|
|
47
|
+
:persist_file => Rails.root.join('log', 'qwirk_persist.yml'),
|
|
48
|
+
:worker_file => Rails.root.join('config', 'qwirk_workers.yml'),
|
|
49
|
+
:stop_on_signal => true,
|
|
50
|
+
:env => env,
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
raise 'Qwirk not configured' unless @@config && @@config[key]
|
|
55
|
+
@@hash[key] ||= Qwirk::Adapter.new(@@config[key])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.fail_queue_name(queue_name)
|
|
59
|
+
return "#{queue_name.to_s}Fail"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
require 'qwirk/engine' if defined?(Rails)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
|
|
3
|
+
# Defines the queuing strategy. Currently, only JMS and InMem.
|
|
4
|
+
class Adapter
|
|
5
|
+
include Rumx::Bean
|
|
6
|
+
|
|
7
|
+
attr_reader :config, :log_times, :adapter_info
|
|
8
|
+
|
|
9
|
+
def initialize(config)
|
|
10
|
+
@config = config.dup
|
|
11
|
+
@log_times = config.delete(:log_times)
|
|
12
|
+
|
|
13
|
+
adapter = config.delete(:adapter)
|
|
14
|
+
raise "No adapter definition" unless adapter
|
|
15
|
+
namespace = Qwirk::QueueAdapter.const_get(adapter)
|
|
16
|
+
@adapter_info = nil
|
|
17
|
+
if namespace.respond_to?(:init)
|
|
18
|
+
@adapter_info = namespace.init(config)
|
|
19
|
+
end
|
|
20
|
+
@publisher_klass = namespace.const_get('Publisher')
|
|
21
|
+
@worker_config_klass = namespace.const_get('WorkerConfig')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def create_publisher(options={})
|
|
25
|
+
@publisher_parent ||= Rumx::Beans::Folder.new
|
|
26
|
+
publisher = Publisher.new(self, @config.merge(options))
|
|
27
|
+
@publisher_parent.bean_add_child(publisher.to_s, publisher)
|
|
28
|
+
return publisher
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_manager(options={})
|
|
32
|
+
manager = Manager.new(self, @config.merge(options))
|
|
33
|
+
bean_add_child(:Workers, manager)
|
|
34
|
+
return manager
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create_adapter_publisher(queue_name, topic_name, options, response_options)
|
|
38
|
+
@publisher_klass.new(self, queue_name, topic_name, options, response_options)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def create_adapter_worker_config(parent, queue_name, topic_name, options, response_options)
|
|
42
|
+
@worker_config_klass.new(self, parent, queue_name, topic_name, options, response_options)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'rumx'
|
|
2
|
+
|
|
3
|
+
module Qwirk
|
|
4
|
+
# TODO: Is this necessary anymore or just put in worker.rb? Decide when flat file queue_adapter is implemented.
|
|
5
|
+
module BaseWorker
|
|
6
|
+
|
|
7
|
+
attr_accessor :index, :thread, :config
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def default_name
|
|
11
|
+
name = self.name.sub(/Worker$/, '')
|
|
12
|
+
name.sub(/::/, '_')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Dynamic class create form WorkerConfig and extended through config_accessor, etc calls that will be defined in the worker
|
|
16
|
+
def config_class
|
|
17
|
+
@config_class ||= Class.new(WorkerConfig)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Default values for all the config attributes
|
|
21
|
+
def default_config
|
|
22
|
+
@default_config ||= WorkerConfig.initial_default_config
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#config_accessor :sleep_time, :float, 'Number of seconds to sleep between messages', 5
|
|
26
|
+
def config_accessor(name, type, description, default_value=nil)
|
|
27
|
+
make_bean_attr(:bean_attr_accessor, name, type, description, default_value)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def config_reader(name, type, description, default_value=nil)
|
|
31
|
+
make_bean_attr(:bean_attr_reader, name, type, description, default_value)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def config_writer(name, type, description, default_value=nil)
|
|
35
|
+
make_bean_attr(:bean_attr_writer, name, type, description, default_value)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def define_configs(configs)
|
|
39
|
+
@configs = configs
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def each_config(&block)
|
|
43
|
+
# Configs are either defined with a define_configs call or default to a single instance with default_config
|
|
44
|
+
if @configs
|
|
45
|
+
@configs.each do |name, config|
|
|
46
|
+
yield name, default_config.merge(config)
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
yield default_name, default_config
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
#######
|
|
54
|
+
private
|
|
55
|
+
#######
|
|
56
|
+
|
|
57
|
+
def make_bean_attr(attr_method, name, type, description, default_value)
|
|
58
|
+
config_class.send(attr_method, name, type, description, :config_item => true)
|
|
59
|
+
default_config[name.to_sym] = default_value
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.included(base)
|
|
64
|
+
Rumx::Bean.included(base)
|
|
65
|
+
base.extend(ClassMethods)
|
|
66
|
+
if base.kind_of?(Class)
|
|
67
|
+
@worker_classes ||= []
|
|
68
|
+
@worker_classes << base unless @worker_classes.include?(base)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.worker_classes
|
|
73
|
+
@worker_classes ||= []
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def start
|
|
77
|
+
raise "Need to override start method in #{self.class.name}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def stop
|
|
81
|
+
raise "Need to override stop method in #{self.class.name}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def join
|
|
85
|
+
thread.join
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def status
|
|
89
|
+
raise "Need to override status method in #{self.class.name}"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def to_s
|
|
93
|
+
"#{config.name}:#{index}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/lib/qwirk/batch.rb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
module Batch
|
|
3
|
+
class AcquireFileStrategy
|
|
4
|
+
def initialize(options)
|
|
5
|
+
@glob = options[:glob]
|
|
6
|
+
raise "file_options glob value not set" unless @glob
|
|
7
|
+
@poll_time = (options[:poll_time] || 10.0).to_f
|
|
8
|
+
# Ftp's could be in progress, make sure the file is at least 60 seconds old by default before processing
|
|
9
|
+
@age = (options[:age] || 60).to_i
|
|
10
|
+
@stopped = false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns the next file or nil if stopped
|
|
14
|
+
def next_file
|
|
15
|
+
until @stopped
|
|
16
|
+
Dir.glob(@glob).each do |file|
|
|
17
|
+
unless file.match /\.(processing|completed)$/
|
|
18
|
+
return file if (Time.now - File.mtime(file) > @age)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
@sleep_thread = Thread.current
|
|
22
|
+
sleep @poll_time
|
|
23
|
+
end
|
|
24
|
+
return nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def mark_file_as_processing(file)
|
|
28
|
+
new_file = file + '.processing'
|
|
29
|
+
File.rename(file, new_file)
|
|
30
|
+
return new_file
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def complete_file(file)
|
|
34
|
+
file.match(/(.*)\.processing$/) || raise("#{file} is not currently being processed")
|
|
35
|
+
new_file = $1 + '.completed'
|
|
36
|
+
File.rename(file, new_file)
|
|
37
|
+
return new_file
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def stop
|
|
41
|
+
@stopped = true
|
|
42
|
+
@sleep_thread.wakeup if @sleep_thread
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
module Qwirk::Batch::ActiveRecord
|
|
2
|
+
class BatchJob < ActiveRecord::Base
|
|
3
|
+
include Qwirk::Batch::JobStatus
|
|
4
|
+
|
|
5
|
+
set_table_name 'mt_batch_jobs'
|
|
6
|
+
|
|
7
|
+
has_many :failed_records, :dependent => :destroy
|
|
8
|
+
has_many :outstanding_records, :dependent => :destroy
|
|
9
|
+
|
|
10
|
+
validates :file, :worker_name, :finished_count, :status, :presence => true
|
|
11
|
+
#t.string :file, :null => false
|
|
12
|
+
#t.string :worker_name, :null => false
|
|
13
|
+
#t.integer :total_count
|
|
14
|
+
#t.integer :finished_count, :null => false, :default => 0
|
|
15
|
+
#t.column :status, 'char(8)', :null => false, :default => Qwirk::Batch::JobStatus::INITED
|
|
16
|
+
#t.datetime :created_at, :null => false
|
|
17
|
+
#t.datetime :updated_at, :null => false
|
|
18
|
+
|
|
19
|
+
# Acquire this file if it hasn't already been acquired.
|
|
20
|
+
def self.acquire(file, worker_name)
|
|
21
|
+
return nil if find_by_file_and_worker_name(file, worker_name)
|
|
22
|
+
create!(:file => file, :worker_name => worker_name)
|
|
23
|
+
rescue ActiveRecord::ActiveRecordError => e
|
|
24
|
+
Rails.logger.warn("Assuming race condition (duplicate index) for BatchJob file=#{file} worker=#{worker_name}: #{e.message}")
|
|
25
|
+
return nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Acquire and resume a paused job if available
|
|
29
|
+
def self.resume_paused_job(worker_name)
|
|
30
|
+
transaction do
|
|
31
|
+
job = where(:worker_name => worker_name, status => PAUSED).lock(true).first
|
|
32
|
+
return nil unless job
|
|
33
|
+
job.outstanding_records.each do |record|
|
|
34
|
+
job.start_record(record.file_position)
|
|
35
|
+
record.destroy
|
|
36
|
+
end
|
|
37
|
+
job.update_attribute(:status => RUNNING)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def initialize(opts={})
|
|
42
|
+
super
|
|
43
|
+
@outstanding_array = []
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def run(total_count)
|
|
47
|
+
update_attributes(:status => RUNNING, :total_count => total_count)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def pause
|
|
51
|
+
save_outstanding_array
|
|
52
|
+
update_attribute(:status => STOPPED)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def abort
|
|
56
|
+
save_outstanding_array
|
|
57
|
+
update_attribute(:status => ABORTED)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def cancel
|
|
61
|
+
save_outstanding_array
|
|
62
|
+
update_attribute(:status => CANCELED)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def finish
|
|
66
|
+
update_attribute(:status => FINISHED)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def start_record(file_position)
|
|
70
|
+
@outstanding_array << file_position
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def finish_record(file_position)
|
|
74
|
+
@outstanding_array.delete(file_position)
|
|
75
|
+
update_attribute(:finished_count => finished_count + 1)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def fail_record(file_position, message)
|
|
79
|
+
@outstanding_array.delete(file_position)
|
|
80
|
+
failed_records.create!(:file_position => file_position, :message => message)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def retry_failed_record
|
|
84
|
+
failed_record = failed_records.first
|
|
85
|
+
return nil unless failed_record
|
|
86
|
+
failed_record.destroy
|
|
87
|
+
return failed_record.file_position
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def outstanding_array
|
|
91
|
+
@outstanding_array
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def failed_hash
|
|
95
|
+
hash = {}
|
|
96
|
+
failed_records.each do |failed_record|
|
|
97
|
+
hash[failed_record.file_position] = failed_record.message
|
|
98
|
+
end
|
|
99
|
+
return hash
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def save_outstanding_array
|
|
105
|
+
outstanding_records.each {|record| record.destroy}
|
|
106
|
+
@outstanding_array.each do |file_position|
|
|
107
|
+
outstanding_records.create!(:file_position => file_position)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|