ap4r 0.3.2 → 0.3.3
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/{CHANGELOG → History.txt} +5 -0
- data/Manifest.txt +48 -0
- data/README.txt +82 -0
- data/Rakefile +71 -149
- data/config/queues_mysql.cfg +2 -2
- data/config/queues_pgsql.cfg +19 -0
- data/lib/ap4r.rb +0 -1
- data/lib/ap4r/message_store_ext.rb +230 -0
- data/lib/ap4r/mongrel_ap4r.rb +4 -0
- data/lib/ap4r/postgresql.sql +13 -0
- data/lib/ap4r/retention_history.rb +2 -2
- data/lib/ap4r/script/setup.rb +0 -1
- data/lib/ap4r/stored_message.rb +120 -7
- data/lib/ap4r/version.rb +1 -1
- data/lib/ap4r/xxx_create_table_for_saf.rb +21 -0
- data/lib/ap4r/xxx_create_table_for_saf_to_postgresql.rb +21 -0
- data/rails_plugin/ap4r/init.rb +11 -0
- data/rails_plugin/ap4r/lib/ap4r/queue_put_stub.rb +21 -0
- data/rails_plugin/ap4r/lib/ap4r/service_handler.rb +165 -0
- data/rails_plugin/ap4r/lib/ap4r_client.rb +132 -0
- data/rails_plugin/ap4r/lib/{async_controller.rb → async_helper.rb} +43 -71
- data/rails_plugin/ap4r/lib/message_builder.rb +181 -0
- data/rails_plugin/ap4r/tasks/ap4r.rake +35 -0
- data/spec/local/dispatcher_base_spec.rb +130 -0
- data/spec/spec_helper.rb +7 -0
- metadata +36 -40
- data/README +0 -55
- data/script/loop.cmd +0 -3
- data/script/loop.rb +0 -8
data/lib/ap4r/mongrel_ap4r.rb
CHANGED
@@ -6,6 +6,8 @@ require 'rubygems'
|
|
6
6
|
require 'mongrel'
|
7
7
|
require 'ap4r/mongrel'
|
8
8
|
|
9
|
+
Mongrel::Command::BANNER.gsub!("rails", "ap4r")
|
10
|
+
|
9
11
|
module Ap4r::Mongrel
|
10
12
|
|
11
13
|
module Command
|
@@ -17,6 +19,8 @@ module Ap4r::Mongrel
|
|
17
19
|
klass.class_eval do |k|
|
18
20
|
include(::Mongrel::Command::Base)
|
19
21
|
end
|
22
|
+
|
23
|
+
|
20
24
|
# TODO: Can subclass the return of RubyGems::GemPlugin ? 2007/04/16 by shino
|
21
25
|
end
|
22
26
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
CREATE TABLE reliable_msg_queues (
|
2
|
+
id character varying(255) NOT NULL default '',
|
3
|
+
queue character varying(255) NOT NULL default '',
|
4
|
+
headers text NOT NULL,
|
5
|
+
object text NOT NULL,
|
6
|
+
PRIMARY KEY (id)
|
7
|
+
);
|
8
|
+
CREATE TABLE reliable_msg_topics (
|
9
|
+
topic character varying(255) NOT NULL default '',
|
10
|
+
headers text NOT NULL,
|
11
|
+
object text NOT NULL,
|
12
|
+
PRIMARY KEY (topic)
|
13
|
+
);
|
data/lib/ap4r/script/setup.rb
CHANGED
data/lib/ap4r/stored_message.rb
CHANGED
@@ -5,12 +5,12 @@
|
|
5
5
|
begin
|
6
6
|
require 'active_record'
|
7
7
|
require 'uuid'
|
8
|
+
require 'reliable-msg'
|
8
9
|
rescue LoadError
|
9
10
|
require 'rubygems'
|
10
|
-
gem 'activerecord'
|
11
11
|
require 'activerecord'
|
12
|
-
gem 'uuid'
|
13
12
|
require 'uuid'
|
13
|
+
require 'reliable-msg'
|
14
14
|
end
|
15
15
|
|
16
16
|
module Ap4r
|
@@ -23,11 +23,30 @@ module Ap4r
|
|
23
23
|
|
24
24
|
STATUS_STORED = 0
|
25
25
|
STATUS_FORWARDED = 1
|
26
|
-
|
27
|
-
|
26
|
+
@@status_value_of = { :unforwarded => STATUS_STORED,
|
27
|
+
:forwarded => STATUS_FORWARDED }
|
28
|
+
|
28
29
|
PHYSICAL = :physical
|
29
30
|
LOGICAL = :logical
|
30
|
-
|
31
|
+
|
32
|
+
def dumped_headers
|
33
|
+
# The warning occurs when putting backslash into binaly type in PostgreSQL.
|
34
|
+
if "postgresql" == Ap4r::StoredMessage.establish_connection.config[:adapter]
|
35
|
+
self.headers
|
36
|
+
else
|
37
|
+
Marshal::dump(self.headers)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def dumped_object
|
42
|
+
# The warning occurs when putting backslash into binaly type in PostgreSQL.
|
43
|
+
if "postgresql" == Ap4r::StoredMessage.establish_connection.config[:adapter]
|
44
|
+
self.object
|
45
|
+
else
|
46
|
+
Marshal::dump(self.object)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
31
50
|
# Insert queue information, such as queue name and message, for next logic.
|
32
51
|
#
|
33
52
|
# duplication_check_id is generated from UUID and should be unique
|
@@ -42,8 +61,15 @@ module Ap4r
|
|
42
61
|
s.duplication_check_id = UUID.new
|
43
62
|
s.queue = queue_name
|
44
63
|
s.status = STATUS_STORED
|
45
|
-
|
46
|
-
|
64
|
+
|
65
|
+
# The warning occurs when putting backslash into binaly type in PostgreSQL.
|
66
|
+
if "postgresql" == Ap4r::StoredMessage.establish_connection.config[:adapter]
|
67
|
+
s.object = YAML.dump(queue_message)
|
68
|
+
s.headers = YAML.dump(rm_options)
|
69
|
+
else
|
70
|
+
s.object = Marshal::dump(queue_message)
|
71
|
+
s.headers = Marshal::dump(rm_options)
|
72
|
+
end
|
47
73
|
end
|
48
74
|
|
49
75
|
begin
|
@@ -86,5 +112,92 @@ module Ap4r
|
|
86
112
|
self
|
87
113
|
end
|
88
114
|
|
115
|
+
# List the records which have specified status.
|
116
|
+
# The statuses are :forwarded, :unforwarded and :all.
|
117
|
+
# :unforwarded means unprocessed or error during forward process.
|
118
|
+
def self.find_status_of(status = :unforwarded)
|
119
|
+
case status
|
120
|
+
when :all
|
121
|
+
StoredMessage.find(:all)
|
122
|
+
when :forwarded, :unforwarded
|
123
|
+
StoredMessage.find(:all, :conditions => { :status => @@status_value_of[status] })
|
124
|
+
else
|
125
|
+
puts "Undefined status: #{status.to_s}."
|
126
|
+
puts "Usage: Ap4r::StoredMessage.find_on [ :forwarded | :unforwarded | :all ]"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Return id, queue_name and created date time.
|
131
|
+
def to_summary_string
|
132
|
+
return "#{self.id}, #{self.queue}, #{self.created_at}"
|
133
|
+
end
|
134
|
+
|
135
|
+
# Update status value.
|
136
|
+
def self.update_status(id, status)
|
137
|
+
return "undefined status: #{status}" unless @@status_value_of.keys.include? status
|
138
|
+
stored_message = StoredMessage.find(id)
|
139
|
+
|
140
|
+
before_status = stored_message.status
|
141
|
+
after_status = @@status_value_of[status]
|
142
|
+
|
143
|
+
stored_message.status = after_status
|
144
|
+
stored_message.save!
|
145
|
+
end
|
146
|
+
|
147
|
+
# Try to forward the ONE message which status is unforwarded.
|
148
|
+
# If the message is forwarded successfully, the status will be "1" that means forwarded.
|
149
|
+
def self.reforward(id)
|
150
|
+
stored_message = StoredMessage.find(id)
|
151
|
+
if stored_message.status == @@status_value_of[:forwarded]
|
152
|
+
raise "The message (id = #{id}) was already forwarded."
|
153
|
+
end
|
154
|
+
stored_message.forward_and_update_status
|
155
|
+
end
|
156
|
+
|
157
|
+
# Try to forward all messages which status are unforwarded.
|
158
|
+
# This method issue commit command to database every transaction_num.
|
159
|
+
def self.reforward_all(transaction_num = 10)
|
160
|
+
|
161
|
+
stored_messages = StoredMessage.find(:all,
|
162
|
+
:conditions => {:status => @@status_value_of[:unforwarded]})
|
163
|
+
total_num = stored_messages.size
|
164
|
+
failed_num = 0
|
165
|
+
|
166
|
+
0.step(total_num, transaction_num) do |offset|
|
167
|
+
target_sms = stored_messages[offset..(offset + transaction_num - 1)]
|
168
|
+
next if target_sms.empty?
|
169
|
+
begin
|
170
|
+
StoredMessage.transaction do
|
171
|
+
target_sms.each do |target_sm|
|
172
|
+
target_sm.forward_and_update_status
|
173
|
+
end
|
174
|
+
end
|
175
|
+
rescue Exception => error
|
176
|
+
puts error.message
|
177
|
+
failed_num += target_sms.size
|
178
|
+
end
|
179
|
+
end
|
180
|
+
return [total_num - failed_num, failed_num]
|
181
|
+
end
|
182
|
+
|
183
|
+
def forward_and_update_status
|
184
|
+
queue_name = self.queue
|
185
|
+
|
186
|
+
# The warning occurs when putting backslash into binaly type in PostgreSQL.
|
187
|
+
if "postgresql" == Ap4r::StoredMessage.establish_connection.config[:adapter]
|
188
|
+
queue_headers = YAML.load(self.headers)
|
189
|
+
queue_messages = YAML.load(self.object)
|
190
|
+
else
|
191
|
+
queue_headers = Marshal::load(self.headers)
|
192
|
+
queue_messages = Marshal::load(self.object)
|
193
|
+
end
|
194
|
+
|
195
|
+
q = ::ReliableMsg::Queue.new(queue_name, :drb_uri => Ap4r::AsyncHelper::Base::DRUBY_URI)
|
196
|
+
q.put(queue_messages, queue_headers)
|
197
|
+
|
198
|
+
self.status = STATUS_FORWARDED
|
199
|
+
self.save!
|
200
|
+
end
|
201
|
+
|
89
202
|
end
|
90
203
|
end
|
data/lib/ap4r/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Author:: Kiwamu Kato
|
2
|
+
# Copyright:: Copyright (c) 2006 Future System Consulting Corp.
|
3
|
+
# Licence:: MIT Licence
|
4
|
+
|
5
|
+
class CreateTableForSaf < ActiveRecord::Migration
|
6
|
+
def self.up
|
7
|
+
create_table :stored_messages do |t|
|
8
|
+
t.column :duplication_check_id, :string, :null => false
|
9
|
+
t.column :queue, :string, :null => false
|
10
|
+
t.column :headers, :binary, :null => false
|
11
|
+
t.column :object, :binary, :null => false
|
12
|
+
t.column :status, :integer, :null => false
|
13
|
+
t.column :created_at, :datetime, :null => false
|
14
|
+
t.column :updated_at, :datetime, :null => false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.down
|
19
|
+
drop_table :stored_messages
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Author:: Kiwamu Kato
|
2
|
+
# Copyright:: Copyright (c) 2006 Future System Consulting Corp.
|
3
|
+
# Licence:: MIT Licence
|
4
|
+
|
5
|
+
class CreateTableForSaf < ActiveRecord::Migration
|
6
|
+
def self.up
|
7
|
+
create_table :stored_messages do |t|
|
8
|
+
t.column :duplication_check_id, :string, :null => false
|
9
|
+
t.column :queue, :string, :null => false
|
10
|
+
t.column :headers, :text, :null => false
|
11
|
+
t.column :object, :text, :null => false
|
12
|
+
t.column :status, :integer, :null => false
|
13
|
+
t.column :created_at, :datetime, :null => false
|
14
|
+
t.column :updated_at, :datetime, :null => false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.down
|
19
|
+
drop_table :stored_messages
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Author:: Shunichi Shinohara
|
2
|
+
# Copyright:: Copyright (c) 2007 Future Architect Inc.
|
3
|
+
# Licence:: MIT Licence
|
4
|
+
|
5
|
+
module Ap4r
|
6
|
+
module AsyncHelper
|
7
|
+
module Base
|
8
|
+
def queued_messages
|
9
|
+
return @queued_messages if @queued_messages
|
10
|
+
@queued_messages = Hash.new {|hash, key| hash[key] = []}
|
11
|
+
return @queued_messages
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def __queue_put(queue_name, message, headers)
|
16
|
+
queued_messages[queue_name] << {:headers => headers, :body => message}
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# Author:: Shunichi Shinohara
|
2
|
+
# Copyright:: Copyright (c) 2007 Future Architect Inc.
|
3
|
+
# Licence:: MIT Licence
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'erb'
|
7
|
+
require 'yaml'
|
8
|
+
require 'reliable-msg'
|
9
|
+
require 'ap4r'
|
10
|
+
require 'timeout'
|
11
|
+
|
12
|
+
module Ap4r
|
13
|
+
|
14
|
+
class ServiceHandler
|
15
|
+
|
16
|
+
def initialize(config_file = RAILS_ROOT + "/config/test_async.yml")
|
17
|
+
raise "please create config/test_async.yml to configure ap4r service." unless File.exist?(config_file)
|
18
|
+
|
19
|
+
config = {}
|
20
|
+
File.open(config_file, "r") do |input|
|
21
|
+
YAML.load_documents(ERB.new(input.read).result) do |doc|
|
22
|
+
config.merge! doc
|
23
|
+
end
|
24
|
+
end
|
25
|
+
@test_config = config["ap4r"]
|
26
|
+
@root_dir = @test_config["root_dir"]
|
27
|
+
@config_file = @test_config["config_file"]
|
28
|
+
@test_server_config = ReliableMsg::Config.new(File.join(@root_dir, @config_file))
|
29
|
+
raise "config file #{@test_server_config.path} NOT exist!" unless @test_server_config.exist?
|
30
|
+
|
31
|
+
@test_server_config.load_no_create
|
32
|
+
@qm = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def qm
|
36
|
+
@qm ||= DRbObject.new_with_uri("druby://localhost:#{@test_server_config.drb["port"]}")
|
37
|
+
end
|
38
|
+
|
39
|
+
# Starts ap4r service.
|
40
|
+
def start_ap4r_service(wait_until_started = true)
|
41
|
+
command = "ruby #{@test_config["start_ruby_args"]} #{@root_dir}/script/mongrel_ap4r " +
|
42
|
+
"start -d -c #{@root_dir} -A #{@config_file} -p 13038"
|
43
|
+
message = "Starting Mongrel(AP4R)"
|
44
|
+
execute_command(command, message, false)
|
45
|
+
if wait_until_started
|
46
|
+
print "and waiting..."
|
47
|
+
wait_until_alive
|
48
|
+
end
|
49
|
+
puts "Done."
|
50
|
+
end
|
51
|
+
|
52
|
+
# Stops ap4r service.
|
53
|
+
def stop_ap4r_service
|
54
|
+
command = "ruby #{@test_config["stop_ruby_args"]} #{@root_dir}/script/mongrel_ap4r " +
|
55
|
+
"stop -c #{@root_dir}"
|
56
|
+
message = "Terminating Mongrel(AP4R)"
|
57
|
+
execute_command(command, message, false)
|
58
|
+
@qm = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
# Starts rails service.
|
62
|
+
# Invokes mongrel_rails, so mongrel_rails should be installed.
|
63
|
+
def start_rails_service
|
64
|
+
# TODO: Can use script/server? It's more general. 2007/05/31 by shino
|
65
|
+
command = "mongrel_rails start -d --environment test"
|
66
|
+
message = "Starting Mongrel(Rails)"
|
67
|
+
execute_command(command, message)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Stops rails service.
|
71
|
+
def stop_rails_service
|
72
|
+
# puts "read pid"
|
73
|
+
# pid = File.read('log/mongrel.pid').to_i
|
74
|
+
# puts "send signal to #{pid}"
|
75
|
+
# Process.kill(:TERM, `cat log/mongrel.pid`.to_i)
|
76
|
+
# puts "done"
|
77
|
+
# return
|
78
|
+
command = "mongrel_rails stop"
|
79
|
+
# command = "kill " + `cat log/mongrel.pid`
|
80
|
+
# command = "sh -c 'mongrel_rails stop'"
|
81
|
+
message = "Terminating Mongrel(Rails)"
|
82
|
+
execute_command(command, message, false)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Starts rails service and ap4r service.
|
86
|
+
# After block execution, stops both.
|
87
|
+
def with_services
|
88
|
+
begin
|
89
|
+
start_rails_service
|
90
|
+
begin
|
91
|
+
start_ap4r_service
|
92
|
+
yield
|
93
|
+
ensure
|
94
|
+
stop_ap4r_service
|
95
|
+
end
|
96
|
+
ensure
|
97
|
+
stop_rails_service
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def start_dispatchers
|
102
|
+
qm.dispatchers.start
|
103
|
+
end
|
104
|
+
|
105
|
+
def stop_dispatchers
|
106
|
+
qm.dispatchers.stop
|
107
|
+
end
|
108
|
+
|
109
|
+
def clear(*queues)
|
110
|
+
raise "not yet implemented"
|
111
|
+
queues.each do |queue|
|
112
|
+
q = ReliableMsg::Queue.new(queue)
|
113
|
+
loop do
|
114
|
+
break unless q.get
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def wait_for_saf_forward
|
120
|
+
50.times do
|
121
|
+
count = ::Ap4r::StoredMessage.count(:conditions => {:status => ::Ap4r::StoredMessage::STATUS_STORED})
|
122
|
+
break if count == 0
|
123
|
+
sleep 0.2
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def wait_all_done
|
128
|
+
50.times do
|
129
|
+
break if flag = qm.no_active_message?
|
130
|
+
sleep 0.2
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def dlq
|
135
|
+
qm.list :queue => "$dlq"
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
def execute_command(command, message, with_done_message = true)
|
140
|
+
command += ";" # force to execute via shell
|
141
|
+
print "#{message} with command: #{command}..."
|
142
|
+
begin
|
143
|
+
timeout(10) do
|
144
|
+
result = system("#{command}")
|
145
|
+
# TODO: handle result 2007/08/29 by shino
|
146
|
+
puts "Done." if with_done_message
|
147
|
+
end
|
148
|
+
rescue TimeoutError
|
149
|
+
puts "!!! command timed out !!!"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def wait_until_alive(message = nil)
|
154
|
+
50.times do
|
155
|
+
print message if message
|
156
|
+
begin
|
157
|
+
break if qm.alive?
|
158
|
+
rescue => e
|
159
|
+
# ignore
|
160
|
+
end
|
161
|
+
sleep 0.2
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|