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