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/{CHANGELOG → History.txt}
RENAMED
@@ -1,5 +1,10 @@
|
|
1
1
|
== 0.3.x
|
2
2
|
|
3
|
+
=== 0.3.3 (June ?, 2007)
|
4
|
+
* Added: Support with hoe.
|
5
|
+
* Added: Support PostgreSQL as reliable RDBMS persistence.
|
6
|
+
* Added: Test supports, stub of queueing and service controls.
|
7
|
+
|
3
8
|
=== 0.3.2 (June 7th, 2007)
|
4
9
|
* Fixed: util/loc.rb doesn't work.
|
5
10
|
* Changed: Argument order of async_dispatch has changed, backward INCOMPATIBLE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
History.txt
|
2
|
+
MIT-LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
bin/ap4r_setup
|
7
|
+
config/ap4r_settings.rb
|
8
|
+
config/log4r.yaml
|
9
|
+
config/queues.cfg
|
10
|
+
config/queues_disk.cfg
|
11
|
+
config/queues_mysql.cfg
|
12
|
+
config/queues_pgsql.cfg
|
13
|
+
lib/ap4r.rb
|
14
|
+
lib/ap4r/carrier.rb
|
15
|
+
lib/ap4r/dispatcher.rb
|
16
|
+
lib/ap4r/message_store_ext.rb
|
17
|
+
lib/ap4r/mongrel.rb
|
18
|
+
lib/ap4r/mongrel_ap4r.rb
|
19
|
+
lib/ap4r/multi_queue.rb
|
20
|
+
lib/ap4r/postgresql.sql
|
21
|
+
lib/ap4r/queue_manager_ext.rb
|
22
|
+
lib/ap4r/queue_manager_ext_debug.rb
|
23
|
+
lib/ap4r/retention_history.rb
|
24
|
+
lib/ap4r/script/base.rb
|
25
|
+
lib/ap4r/script/queue_manager_control.rb
|
26
|
+
lib/ap4r/script/setup.rb
|
27
|
+
lib/ap4r/script/workspace_generator.rb
|
28
|
+
lib/ap4r/start_with_log4r.rb
|
29
|
+
lib/ap4r/store_and_forward.rb
|
30
|
+
lib/ap4r/stored_message.rb
|
31
|
+
lib/ap4r/util/irm.rb
|
32
|
+
lib/ap4r/util/queue_client.rb
|
33
|
+
lib/ap4r/version.rb
|
34
|
+
lib/ap4r/xxx_create_table_for_saf.rb
|
35
|
+
lib/ap4r/xxx_create_table_for_saf_to_postgresql.rb
|
36
|
+
rails_plugin/ap4r/init.rb
|
37
|
+
rails_plugin/ap4r/lib/ap4r/queue_put_stub.rb
|
38
|
+
rails_plugin/ap4r/lib/ap4r/service_handler.rb
|
39
|
+
rails_plugin/ap4r/lib/ap4r_client.rb
|
40
|
+
rails_plugin/ap4r/lib/async_helper.rb
|
41
|
+
rails_plugin/ap4r/lib/message_builder.rb
|
42
|
+
rails_plugin/ap4r/tasks/ap4r.rake
|
43
|
+
script/irm
|
44
|
+
script/mongrel_ap4r
|
45
|
+
script/start
|
46
|
+
script/stop
|
47
|
+
spec/local/dispatcher_base_spec.rb
|
48
|
+
spec/spec_helper.rb
|
data/README.txt
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
Ap4r
|
2
|
+
* by Kiwamu Kato, Shun'ichi Shinohara
|
3
|
+
* http://ap4r.rubyforge.org/wiki/wiki.pl?HomePage
|
4
|
+
* ap4r-user@rubyforge.org
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
AP4R, Asynchronous Processing for Ruby, is the implementation of reliable asynchronous message processing. It provides message queuing, and message dispatching.
|
9
|
+
Using asynchronous processing, we can cut down turn-around-time of web applications by queuing, or can utilize more machine power by load-balancing.
|
10
|
+
Also AP4R nicely ties with your Ruby on Rails applications. See Hello World sample application from rubyforge.
|
11
|
+
|
12
|
+
For more information, please step in AP4R homepage!
|
13
|
+
|
14
|
+
|
15
|
+
== FEATURES / PROBLEMS TO SOLVE:
|
16
|
+
|
17
|
+
* Business logics can be implemented as simple Web applications, or ruby code, whether it's called asynchronously or synchronously.
|
18
|
+
* Asynchronous messaging is reliable by RDBMS persistence (now MySQL only) or file persistence, under the favor of reliable-msg.
|
19
|
+
* Load balancing over multiple AP4R processes on single/multiple servers is supported.
|
20
|
+
* Asynchronous logics are called via various protocols, such as XML-RPC, SOAP, HTTP POST, and more.
|
21
|
+
* Using store and forward function, at-least-once QoS level is provided.
|
22
|
+
|
23
|
+
== TYPICAL PROCESS FLOW:
|
24
|
+
|
25
|
+
1. A client (e.g. a web browser) makes a request to a web server (Apache, Lighttpd, etc...).
|
26
|
+
1. A rails application (a synchronous logic) is executed on mongrel via mod_proxy or something.
|
27
|
+
1. At the last of the synchronous logic, message(s) are put to AP4R (AP4R provides a helper).
|
28
|
+
1. Once the synchronous logic is done, the clients receives a response immediately.
|
29
|
+
1. AP4R queues the message, and requests it to the web server asynchronously.
|
30
|
+
1. An asynchronous logic, implemented as usual rails action, is executed.
|
31
|
+
|
32
|
+
|
33
|
+
== SYNOPSIS:
|
34
|
+
|
35
|
+
* FIX (code sample of usage)
|
36
|
+
|
37
|
+
== REQUIREMENTS:
|
38
|
+
|
39
|
+
* FIX (list of requirements)
|
40
|
+
|
41
|
+
== INSTALL:
|
42
|
+
|
43
|
+
Use RubyGems command.
|
44
|
+
|
45
|
+
$ sudo gem install ap4r --include-dependencies
|
46
|
+
|
47
|
+
|
48
|
+
== REFERENCES:
|
49
|
+
|
50
|
+
* Ruby Homepage
|
51
|
+
* http://www.ruby-lang.org/
|
52
|
+
* Ruby on Rails tutorial
|
53
|
+
* http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html
|
54
|
+
* MySQL tutorial
|
55
|
+
* http://dev.mysql.com/doc/refman/5.0/en/index.html
|
56
|
+
* reliable-msg
|
57
|
+
* http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/ReliableMessaging
|
58
|
+
|
59
|
+
|
60
|
+
== LICENSE:
|
61
|
+
|
62
|
+
* This software is licensed under the MIT license.
|
63
|
+
* Copyright(c) 2007 Future Architect Inc.
|
64
|
+
|
65
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
66
|
+
a copy of this software and associated documentation files (the
|
67
|
+
'Software'), to deal in the Software without restriction, including
|
68
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
69
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
70
|
+
permit persons to whom the Software is furnished to do so, subject to
|
71
|
+
the following conditions:
|
72
|
+
|
73
|
+
The above copyright notice and this permission notice shall be
|
74
|
+
included in all copies or substantial portions of the Software.
|
75
|
+
|
76
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
77
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
78
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
79
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
80
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
81
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
82
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,101 +1,87 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
require File.join(File.dirname(__FILE__), 'lib/ap4r', 'version')
|
4
4
|
|
5
|
-
|
6
|
-
require 'rake/testtask'
|
7
|
-
require 'rake/rdoctask'
|
8
|
-
require 'rake/gempackagetask'
|
9
|
-
require 'rake/contrib/rubyforgepublisher'
|
5
|
+
HelloWorld = '../samples/HelloWorld'
|
10
6
|
|
11
|
-
|
12
|
-
require 'find'
|
13
|
-
require 'rbconfig'
|
7
|
+
# AP4R release tasks --------------------------------------------------------
|
14
8
|
|
15
|
-
|
9
|
+
# copy rails plugin from sample before gem build
|
10
|
+
FileUtils.mkdir_p('./rails_plugin/ap4r/lib')
|
11
|
+
FileUtils.cp(HelloWorld + '/db/migrate/001_create_table_for_saf.rb', './lib/ap4r/xxx_create_table_for_saf.rb')
|
12
|
+
FileUtils.cp_r(Dir.glob(HelloWorld + '/vendor/plugins/ap4r/*').reject{|f| f =~ /tmp$|CVS|\.svn/}, './rails_plugin/ap4r')
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
HELLO_WORLD_DIR = '../samples/HelloWorld'
|
23
|
-
|
24
|
-
# Generate GEM ----------------------------------------------------------------------------
|
25
|
-
|
26
|
-
|
27
|
-
PKG_FILES = FileList[
|
28
|
-
'[a-zA-Z]*',
|
29
|
-
'bin/**/*',
|
30
|
-
'config/**/*',
|
31
|
-
'rails_plugin/**/*',
|
32
|
-
'script/**/*',
|
33
|
-
'lib/**/*'
|
34
|
-
]
|
35
|
-
|
36
|
-
HELLO_WORLD_SAMPLE_FILES = FileList[
|
37
|
-
'[a-zA-Z]*',
|
38
|
-
'app/**/*',
|
39
|
-
'components/**/*',
|
40
|
-
'config/**/*',
|
41
|
-
'db/**/*',
|
42
|
-
'doc/**/*',
|
43
|
-
'lib/**/*',
|
44
|
-
'log/**/*',
|
45
|
-
'public/**/*',
|
46
|
-
'script/**/*',
|
47
|
-
'test/**/*',
|
48
|
-
'tmp/**/*',
|
49
|
-
'vendor/**/*',
|
50
|
-
]
|
51
|
-
|
52
|
-
|
53
|
-
spec = Gem::Specification.new do |s|
|
54
|
-
s.name = 'ap4r'
|
55
|
-
s.version = PKG_VERSION
|
56
|
-
s.summary = "Asynchronous Processing for Ruby."
|
57
|
-
s.description = <<-EOF
|
14
|
+
Hoe.new('ap4r', Ap4r::VERSION::STRING) do |p|
|
15
|
+
p.author = ["Shunichi Shinohara", "Kiwamu Kato"]
|
16
|
+
p.changes = p.paragraphs_of('History.txt', 1..2).join("\n\n")
|
17
|
+
#p.clean_globs =
|
18
|
+
p.description = <<-EOF
|
58
19
|
Asynchronous Processing for Ruby.
|
59
20
|
EOF
|
21
|
+
p.email = %q{shinohara.shunichi@future.co.jp, kato.kiwamu@future.co.jp}
|
22
|
+
|
23
|
+
p.extra_deps << ['reliable-msg', '=1.1.0']
|
24
|
+
p.extra_deps << ['activesupport']
|
25
|
+
p.extra_deps << ['mongrel']
|
26
|
+
p.extra_deps << ['rake']
|
27
|
+
p.extra_deps << ['hoe']
|
28
|
+
|
29
|
+
p.name = 'ap4r'
|
30
|
+
p.need_tar = false
|
31
|
+
p.need_zip = false
|
32
|
+
#p.rdoc_pattern =
|
33
|
+
#p.remote_rdoc_dir =
|
34
|
+
#p.rsync =
|
35
|
+
p.rubyforge_name = 'ap4r'
|
36
|
+
#p.spec_extra =
|
37
|
+
p.summary = 'Asynchronous Processing for Ruby.'
|
38
|
+
p.test_globs = 'spec/**/*_spec.rb'
|
39
|
+
p.url = 'http://ap4r.rubyforge.org/wiki/wiki.pl?HomePage'
|
40
|
+
p.version = Ap4r::VERSION::STRING
|
41
|
+
end.spec.dependencies.delete_if {|dep| dep.name == "hoe"}
|
42
|
+
|
43
|
+
|
44
|
+
desc 'Create Manifest.txt'
|
45
|
+
task :create_manifest do
|
46
|
+
|
47
|
+
path_list = []
|
48
|
+
Find.find('.') do |path|
|
49
|
+
next unless File.file? path
|
50
|
+
next if path =~ /\.svn|tmp$|CVS|.rb\~/
|
51
|
+
path_list << path
|
52
|
+
end
|
60
53
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
s.rdoc_options << "--title" << "Asynchronous Processing for Ruby"
|
70
|
-
s.rdoc_options << "--line-numbers"
|
71
|
-
|
54
|
+
File.open('Manifest.txt', 'w') do |manifest|
|
55
|
+
path_list.sort.each do |path|
|
56
|
+
/.\// =~ path
|
57
|
+
manifest.puts($~.post_match)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
72
62
|
|
73
|
-
s.files = PKG_FILES.to_a.delete_if {|f| f.include?('.svn')}
|
74
|
-
s.require_path = 'lib'
|
75
|
-
s.autorequire = %q{ap4r.rb}
|
76
63
|
|
77
|
-
s.bindir = "bin" # Use these for applications.
|
78
|
-
s.executables = ["ap4r_setup"]
|
79
|
-
s.default_executable = "ap4r_setup"
|
80
64
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
65
|
+
# Sample release tasks ------------------------------------------------------
|
66
|
+
desc 'Make samle tarball (Now only HelloWorld sample).'
|
67
|
+
task :sample do
|
68
|
+
FileUtils.mkdir_p('./pkg/samples')
|
69
|
+
FileUtils.rm_rf('./pkg/samples/HelloWorld')
|
70
|
+
|
71
|
+
FileUtils.cp_r(HelloWorld, './pkg/samples/')
|
72
|
+
Find.find('./pkg/samples') do |path|
|
73
|
+
next unless File.file? path
|
74
|
+
FileUtils.rm_rf(path) if path =~ /\.svn|tmp$|CVS|.rb\~/
|
75
|
+
end
|
86
76
|
|
87
|
-
|
77
|
+
Dir.chdir('./pkg/samples/HelloWorld')
|
78
|
+
`rake db:migrate`
|
79
|
+
Dir.chdir('../../')
|
80
|
+
|
81
|
+
`tar czvf HelloWorld-#{Ap4r::VERSION::STRING}.tar.gz ./samples/HelloWorld/`
|
82
|
+
Dir.chdir('../')
|
88
83
|
end
|
89
84
|
|
90
|
-
# Generate documentation ------------------------------------------------------------------
|
91
|
-
|
92
|
-
Rake::RDocTask.new { |rdoc|
|
93
|
-
rdoc.rdoc_dir = 'doc'
|
94
|
-
rdoc.options << '--line-numbers' << '--inline-source' << '--accessor' << 'cattr_accessor=rw'
|
95
|
-
rdoc.rdoc_files.include('README', 'CHANGELOG')
|
96
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
97
|
-
rdoc.rdoc_files.include('rails_plugin/**/*.rb')
|
98
|
-
}
|
99
85
|
|
100
86
|
# Spec tasks ----------------------------------------------------------------
|
101
87
|
require 'spec/rake/spectask'
|
@@ -123,70 +109,6 @@ namespace :spec do
|
|
123
109
|
end
|
124
110
|
end
|
125
111
|
|
126
|
-
# AP4R release ----------------------------------------------------------------
|
127
|
-
|
128
|
-
desc "Make gem"
|
129
|
-
task :gem_release => [ :make_release_dir, :copy_to_ap4r_from_sample, :gem]
|
130
|
-
|
131
|
-
task :copy_to_ap4r_from_sample do
|
132
|
-
FileUtils.cp(HELLO_WORLD_DIR + '/db/migrate/001_create_table_for_saf.rb', './lib/ap4r/xxx_create_table_for_saf.rb')
|
133
|
-
|
134
|
-
FileUtils.cp(HELLO_WORLD_DIR + '/vendor/plugins/ap4r/init.rb', './rails_plugin/ap4r/init.rb')
|
135
|
-
FileUtils.cp(HELLO_WORLD_DIR + '/vendor/plugins/ap4r/lib/async_helper.rb', './rails_plugin/ap4r/lib/async_helper.rb')
|
136
|
-
FileUtils.cp(HELLO_WORLD_DIR + '/vendor/plugins/ap4r/lib/ap4r_client.rb', './rails_plugin/ap4r/lib/ap4r_client.rb')
|
137
|
-
FileUtils.cp(HELLO_WORLD_DIR + '/vendor/plugins/ap4r/lib/message_builder.rb', './rails_plugin/ap4r/lib/message_builder.rb')
|
138
|
-
end
|
139
|
-
|
140
|
-
desc "Make sample tgz"
|
141
|
-
task :sample_release => [ :make_release_dir, :make_sample_tgz, :copy_to_release_dir ]
|
142
|
-
|
143
|
-
task :make_sample_tgz => [ :make_temp_dir, :copy_sample, :execute_migration, :make_tgz ]
|
144
|
-
|
145
|
-
task :make_release_dir do
|
146
|
-
make_dir RELEASE_DIR
|
147
|
-
end
|
148
|
-
|
149
|
-
task :make_temp_dir do
|
150
|
-
make_dir TEMP_DIR
|
151
|
-
end
|
152
|
-
|
153
|
-
def make_dir(path)
|
154
|
-
if(File.exist?(path))
|
155
|
-
FileUtils.remove_entry(path, true)
|
156
|
-
end
|
157
|
-
FileUtils.mkdir_p(path)
|
158
|
-
end
|
159
|
-
|
160
|
-
|
161
|
-
task :copy_sample do
|
162
|
-
FileUtils.cp_r(HELLO_WORLD_DIR, TEMP_DIR)
|
163
|
-
Find.find(TEMP_DIR) {|f|
|
164
|
-
if f.include?('.svn')
|
165
|
-
FileUtils.rm_rf(f)
|
166
|
-
end
|
167
|
-
}
|
168
|
-
end
|
169
|
-
|
170
|
-
task :execute_migration do
|
171
|
-
Dir.chdir('temp/HelloWorld')
|
172
|
-
`rake db:migrate`
|
173
|
-
Dir.chdir('../../')
|
174
|
-
end
|
175
|
-
|
176
|
-
task :make_tgz do
|
177
|
-
Dir.chdir('temp/')
|
178
|
-
`tar czvf HelloWorld.tar.gz HelloWorld/`
|
179
|
-
Dir.chdir('../')
|
180
|
-
end
|
181
|
-
|
182
|
-
task :copy_to_release_dir do
|
183
|
-
Dir.foreach(PKG_DIR) {|f|
|
184
|
-
FileUtils.cp(PKG_DIR + '/' + f, RELEASE_DIR) if File.fnmatch("*.gem", f)
|
185
|
-
}
|
186
|
-
Dir.foreach(TEMP_DIR) {|f|
|
187
|
-
FileUtils.cp(TEMP_DIR + '/' + f, RELEASE_DIR) if File.fnmatch("*.tar.gz", f)
|
188
|
-
}
|
189
|
-
end
|
190
112
|
|
191
113
|
# AP4R misc tools ----------------------------------------------------------------
|
192
114
|
|
data/config/queues_mysql.cfg
CHANGED
@@ -8,7 +8,7 @@ store:
|
|
8
8
|
drb:
|
9
9
|
host:
|
10
10
|
port: 6438
|
11
|
-
acl: allow 127.0.0.1 allow 10.0.0.0/8
|
11
|
+
acl: allow 127.0.0.1 allow ::1 allow 10.0.0.0/8
|
12
12
|
dispatchers:
|
13
13
|
-
|
14
14
|
targets: queue.*
|
@@ -16,4 +16,4 @@ dispatchers:
|
|
16
16
|
#carriers:
|
17
17
|
# -
|
18
18
|
# source_uri: druby://another.host.local:6438
|
19
|
-
# threads: 1
|
19
|
+
# threads: 1
|
@@ -0,0 +1,19 @@
|
|
1
|
+
---
|
2
|
+
store:
|
3
|
+
type: postgresql
|
4
|
+
uri: # default is tcp://localhost:5432
|
5
|
+
database: ap4r
|
6
|
+
username: ap4r
|
7
|
+
password: ap4r
|
8
|
+
drb:
|
9
|
+
host:
|
10
|
+
port: 6438
|
11
|
+
acl: allow 127.0.0.1 allow ::1 allow 10.0.0.0/8
|
12
|
+
dispatchers:
|
13
|
+
-
|
14
|
+
targets: queue.*
|
15
|
+
threads: 1
|
16
|
+
#carriers:
|
17
|
+
# -
|
18
|
+
# source_uri: druby://another.host.local:6438
|
19
|
+
# threads: 1
|
data/lib/ap4r.rb
CHANGED
@@ -33,6 +33,236 @@ module ReliableMsg #:nodoc:
|
|
33
33
|
|
34
34
|
end
|
35
35
|
|
36
|
+
begin
|
37
|
+
|
38
|
+
# Make sure we have a PostgreSQL library before creating this class,
|
39
|
+
# worst case we end up with a disk-based message store. Try the
|
40
|
+
# native PostgreSQL library, followed by the pure Ruby PostgreSQL library.
|
41
|
+
begin
|
42
|
+
require 'postgres'
|
43
|
+
rescue LoadError
|
44
|
+
require 'postgres-pr/connection'
|
45
|
+
end
|
46
|
+
|
47
|
+
require 'base64'
|
48
|
+
|
49
|
+
class PostgreSQL < Base #:nodoc:
|
50
|
+
|
51
|
+
TYPE = self.name.split('::').last.downcase
|
52
|
+
|
53
|
+
@@stores[TYPE] = self
|
54
|
+
|
55
|
+
# Default prefix for tables in the database.
|
56
|
+
DEFAULT_PREFIX = 'reliable_msg_';
|
57
|
+
|
58
|
+
# Reference to an open PostgreSQL connection held in the current thread.
|
59
|
+
THREAD_CURRENT_PGSQL = :reliable_msg_pgsql #:nodoc:
|
60
|
+
|
61
|
+
def initialize config, logger
|
62
|
+
super logger
|
63
|
+
@config = { :host=>config['host'], :username=>config['username'], :password=>config['password'],
|
64
|
+
:database=>config['database'], :port=>config['port'], :socket=>config['socket'] }
|
65
|
+
@prefix = config['prefix'] || DEFAULT_PREFIX
|
66
|
+
@queues_table = "#{@prefix}queues"
|
67
|
+
@topics_table = "#{@prefix}topics"
|
68
|
+
end
|
69
|
+
|
70
|
+
def type
|
71
|
+
TYPE
|
72
|
+
end
|
73
|
+
|
74
|
+
def setup
|
75
|
+
pgsql = connection
|
76
|
+
requires = 2 # Number of tables used by reliable-msg.
|
77
|
+
pgsql.query "\dt" do |result|
|
78
|
+
while row = result.fetch_row
|
79
|
+
requires -= 1 if row[0] == @queues_table || row[0] == @topics_table
|
80
|
+
end
|
81
|
+
end
|
82
|
+
if requires > 0
|
83
|
+
sql = File.open File.join(File.dirname(__FILE__), "postgresql.sql"), "r" do |input|
|
84
|
+
input.readlines.join
|
85
|
+
end
|
86
|
+
sql.gsub! DEFAULT_PREFIX, @prefix
|
87
|
+
pgsql.query sql
|
88
|
+
true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def configuration
|
94
|
+
config = { "type"=>TYPE, "host"=>@config[:host], "username"=>@config[:username],
|
95
|
+
"password"=>@config[:password], "database"=>@config[:database] }
|
96
|
+
config["port"] = @config[:port] if @config[:port]
|
97
|
+
config["socket"] = @config[:socket] if @config[:socket]
|
98
|
+
config["prefix"] = @config[:prefix] if @config[:prefix]
|
99
|
+
config
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def activate
|
104
|
+
super
|
105
|
+
load_index
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def deactivate
|
110
|
+
Thread.list.each do |thread|
|
111
|
+
if conn = thread[THREAD_CURRENT_PGSQL]
|
112
|
+
thread[THREAD_CURRENT_PGSQL] = nil
|
113
|
+
conn.close
|
114
|
+
end
|
115
|
+
end
|
116
|
+
super
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
protected
|
121
|
+
|
122
|
+
def update inserts, deletes, dlqs
|
123
|
+
pgsql = connection
|
124
|
+
pgsql.query "BEGIN"
|
125
|
+
begin
|
126
|
+
inserts.each do |insert|
|
127
|
+
if insert[:queue]
|
128
|
+
pgsql.query "INSERT INTO #{@queues_table} (id,queue,headers,object) VALUES('#{connection.quote insert[:id]}', '#{connection.quote insert[:queue]}', '#{connection.quote YAML.dump(insert[:headers])}', '#{connection.quote Base64.encode64(insert[:message])}')"
|
129
|
+
else
|
130
|
+
pgsql.query "REPLACE #{@topics_table} (topic,headers,object) VALUES('#{connection.quote insert[:topic]}','#{connection.quote YAML.dump(insert[:headers])}','#{connection.quote insert[:message]}')"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
ids = deletes.inject([]) do |array, delete|
|
134
|
+
delete[:queue] ? array << "'#{delete[:id]}'" : array
|
135
|
+
end
|
136
|
+
if !ids.empty?
|
137
|
+
pgsql.query "DELETE FROM #{@queues_table} WHERE id IN (#{ids.join ','})"
|
138
|
+
end
|
139
|
+
dlqs.each do |dlq|
|
140
|
+
pgsql.query "UPDATE #{@queues_table} SET queue='#{Queue::DLQ}' WHERE id='#{connection.quote dlq[:id]}'"
|
141
|
+
end
|
142
|
+
pgsql.query "COMMIT"
|
143
|
+
rescue Exception=>error
|
144
|
+
pgsql.query "ROLLBACK"
|
145
|
+
raise error
|
146
|
+
end
|
147
|
+
super
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def load_index
|
152
|
+
connection.query "SELECT id,queue,headers FROM #{@queues_table}" do |result|
|
153
|
+
result.each do |tuple|
|
154
|
+
queue = @queues[tuple[1]] ||= []
|
155
|
+
headers = YAML.load tuple[2]
|
156
|
+
# Add element based on priority, higher priority comes first.
|
157
|
+
priority = headers[:priority]
|
158
|
+
added = false
|
159
|
+
queue.each_index do |idx|
|
160
|
+
if queue[idx][:priority] < priority
|
161
|
+
queue[idx, 0] = headers
|
162
|
+
added = true
|
163
|
+
break
|
164
|
+
end
|
165
|
+
end
|
166
|
+
queue << headers unless added
|
167
|
+
end
|
168
|
+
end
|
169
|
+
connection.query "SELECT topic,headers FROM #{@topics_table}" do |result|
|
170
|
+
result.each do |tuple|
|
171
|
+
@topics[tuple[0]] = YAML.load tuple[1]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
def load id, type, queue_or_topic
|
178
|
+
message = nil
|
179
|
+
if type == :queue
|
180
|
+
connection.query "SELECT object FROM #{@queues_table} WHERE id='#{id}'" do |result|
|
181
|
+
message = Base64.decode64(result[0][0]) if result[0]
|
182
|
+
end
|
183
|
+
else
|
184
|
+
connection.query "SELECT object FROM #{@topics_table} WHERE topic='#{queue_or_topic}'" do |result|
|
185
|
+
message = Base64.decode64(result[0][0]) if result[0]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
message
|
189
|
+
end
|
190
|
+
|
191
|
+
def connection
|
192
|
+
Thread.current[THREAD_CURRENT_PGSQL] ||=
|
193
|
+
# PGconn is overriding in this file, so is defined regardless of 'postgres' LoadError.
|
194
|
+
if Object.const_defined? :PGError
|
195
|
+
::PGconn.connect @config[:host], @config[:port], @config[:options], @config[:tty], @config[:database], @config[:username], @config[:password]
|
196
|
+
elsif Object.const_defined? :PostgresPR
|
197
|
+
::PostgresPR::Connection.new @config[:database], @config[:username], @config[:password], @config[:uri]
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
rescue LoadError
|
205
|
+
# do nothing
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
if Object.const_defined? :PGError
|
212
|
+
class PGconn
|
213
|
+
alias original_query query
|
214
|
+
|
215
|
+
def query(q, *bind_values, &block)
|
216
|
+
# In PGconn, +query+ method does NOT care about a given block.
|
217
|
+
# To deal with a given block, this method adds iteration
|
218
|
+
# over query results.
|
219
|
+
maybe_result = exec(q, *bind_values)
|
220
|
+
puts "PGconn: query called by #{q}" if $DEBUG
|
221
|
+
puts "PGconn#query returns #{maybe_result}(class: #{maybe_result.class})." if $DEBUG
|
222
|
+
return maybe_result unless block && maybe_result.kind_of?(PGresult)
|
223
|
+
begin
|
224
|
+
puts "PGconn extention: about to yield result." if $DEBUG
|
225
|
+
block.call(maybe_result)
|
226
|
+
ensure
|
227
|
+
maybe_result.clear
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def quote str
|
232
|
+
# do nothing
|
233
|
+
str
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
if Object.const_defined? :PostgresPR
|
240
|
+
module PostgresPR
|
241
|
+
class Connection
|
242
|
+
alias original_query query
|
243
|
+
|
244
|
+
def query(q, &block)
|
245
|
+
# In PostgresPR, +query+ method does NOT care about a given block.
|
246
|
+
# To deal with a given block, this method adds iteration
|
247
|
+
# over query results.
|
248
|
+
maybe_result = original_query(q, &block)
|
249
|
+
puts "PostgresPR: query called by #{q}" if $DEBUG
|
250
|
+
puts "PostgresPR::Connenction#query returns #{maybe_result}(class: #{maybe_result.class})." if $DEBUG
|
251
|
+
return maybe_result.rows unless block && maybe_result.kind_of?(PostgresPR::Connection::Result)
|
252
|
+
begin
|
253
|
+
puts "PostgresPR extention: about to yield result." if $DEBUG
|
254
|
+
block.call(maybe_result.rows)
|
255
|
+
ensure
|
256
|
+
maybe_result = nil
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def quote(str)
|
261
|
+
# do nothing
|
262
|
+
str
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
36
266
|
end
|
37
267
|
end
|
38
268
|
|