xbar 0.4.1 → 0.4.2
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/.gitignore +1 -0
- data/.rspec +2 -0
- data/README.mkdn +1 -1
- data/Rakefile +5 -2
- data/dummy/.gitignore +15 -0
- data/dummy/Gemfile +38 -0
- data/dummy/README.rdoc +261 -0
- data/dummy/Rakefile +7 -0
- data/dummy/app/assets/images/rails.png +0 -0
- data/dummy/app/assets/javascripts/application.js +15 -0
- data/dummy/app/assets/stylesheets/application.css +13 -0
- data/dummy/app/controllers/application_controller.rb +3 -0
- data/dummy/app/helpers/application_helper.rb +2 -0
- data/dummy/app/mailers/.gitkeep +0 -0
- data/dummy/app/models/.gitkeep +0 -0
- data/dummy/app/views/layouts/application.html.erb +14 -0
- data/dummy/config.ru +4 -0
- data/dummy/config/application.rb +59 -0
- data/dummy/config/boot.rb +6 -0
- data/dummy/config/database.yml +25 -0
- data/dummy/config/environment.rb +5 -0
- data/dummy/config/environments/development.rb +37 -0
- data/dummy/config/environments/production.rb +67 -0
- data/dummy/config/environments/test.rb +37 -0
- data/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/dummy/config/initializers/inflections.rb +15 -0
- data/dummy/config/initializers/mime_types.rb +5 -0
- data/dummy/config/initializers/secret_token.rb +7 -0
- data/dummy/config/initializers/session_store.rb +8 -0
- data/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/dummy/config/locales/en.yml +5 -0
- data/dummy/config/routes.rb +58 -0
- data/dummy/db/schema.rb +16 -0
- data/dummy/db/seeds.rb +7 -0
- data/dummy/doc/README_FOR_APP +2 -0
- data/dummy/lib/assets/.gitkeep +0 -0
- data/dummy/lib/generators/initializer/USAGE +8 -0
- data/dummy/lib/generators/initializer/initializer_generator.rb +8 -0
- data/dummy/lib/generators/initializer/templates/initializer.rb +1 -0
- data/dummy/lib/tasks/.gitkeep +0 -0
- data/dummy/log/.gitkeep +0 -0
- data/dummy/public/404.html +26 -0
- data/dummy/public/422.html +26 -0
- data/dummy/public/500.html +25 -0
- data/dummy/public/favicon.ico +0 -0
- data/dummy/public/index.html +241 -0
- data/dummy/public/robots.txt +5 -0
- data/dummy/script/rails +6 -0
- data/dummy/test/fixtures/.gitkeep +0 -0
- data/dummy/test/functional/.gitkeep +0 -0
- data/dummy/test/integration/.gitkeep +0 -0
- data/dummy/test/performance/browsing_test.rb +12 -0
- data/dummy/test/test_helper.rb +13 -0
- data/dummy/test/unit/.gitkeep +0 -0
- data/dummy/vendor/assets/javascripts/.gitkeep +0 -0
- data/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
- data/dummy/vendor/plugins/.gitkeep +0 -0
- data/examples/README +3 -1
- data/examples/basic.rb +34 -0
- data/examples/config/canada.json +17 -0
- data/examples/config/canada2.json +17 -0
- data/examples/config/connection.rb +2 -0
- data/examples/config/simple.json +10 -10
- data/examples/lib/helpers.rb +90 -0
- data/examples/{setup.rb → lib/setup.rb} +14 -6
- data/examples/migrations/1_create_users.rb +1 -1
- data/examples/multithread.rb +47 -0
- data/examples/pause.rb +67 -0
- data/examples/pause_switch.rb +61 -0
- data/examples/switch.rb +42 -0
- data/gemfiles/rails32.gemfile.lock +3 -3
- data/lib/xbar.rb +1 -0
- data/lib/xbar/mapper.rb +61 -14
- data/lib/xbar/model.rb +1 -10
- data/lib/xbar/proxy.rb +104 -33
- data/lib/xbar/shard.rb +17 -1
- data/lib/xbar/version.rb +1 -1
- data/run +15 -7
- data/spec/console.rb +0 -1
- data/spec/spec_helper.rb +5 -2
- data/spec/xbar/association_spec.rb +1 -0
- data/spec/xbar/mapper_spec.rb +2 -4
- data/spec/xbar/model_spec.rb +11 -8
- data/spec/xbar/proxy_spec.rb +1 -1
- metadata +90 -28
- data/examples/example1.rb +0 -34
@@ -10,18 +10,23 @@ require 'xbar'
|
|
10
10
|
module Examples
|
11
11
|
module Setup
|
12
12
|
|
13
|
-
MIGRATIONS_ROOT = File.expand_path(File.join(File.dirname(__FILE__),
|
13
|
+
MIGRATIONS_ROOT = File.expand_path(File.join(File.dirname(__FILE__),
|
14
|
+
'../migrations'))
|
15
|
+
|
16
|
+
def self.clean
|
17
|
+
# This must agree with what's in the 'simple' JSON config file. Make
|
18
|
+
# sure that we're starting with a clean slate.
|
19
|
+
%x{ rm -f /tmp/store.sqlite3 /tmp/bakery.sqlite3 \
|
20
|
+
/tmp/deli.sqlite3 /tmp/produce.sqlite3 }
|
21
|
+
end
|
14
22
|
|
15
23
|
def self.start(xbar_env, app_env, version = nil)
|
16
24
|
|
17
|
-
|
18
|
-
# sure that we're starting with a clean slate.
|
19
|
-
%x{ rm -f /tmp/paris.sqlite3 /tmp/france_1.sqlite3 \
|
20
|
-
/tmp/france_2.sqlite3 /tmp/france_3.sqlite3 }
|
25
|
+
clean
|
21
26
|
|
22
27
|
# This directory should have a subdirectory called 'config' which
|
23
28
|
# actually holds the config files.
|
24
|
-
XBar.directory = File.expand_path(File.dirname(__FILE__))
|
29
|
+
XBar.directory = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
25
30
|
|
26
31
|
# Initialize the mapper with the 'test' environment from the 'simple'
|
27
32
|
# configuration file.
|
@@ -37,6 +42,9 @@ module Examples
|
|
37
42
|
if version
|
38
43
|
ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT, version)
|
39
44
|
end
|
45
|
+
|
46
|
+
clean
|
47
|
+
|
40
48
|
end
|
41
49
|
|
42
50
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_record'
|
3
|
+
require 'xbar'
|
4
|
+
|
5
|
+
XBar.directory = File.expand_path(File.dirname(__FILE__))
|
6
|
+
XBar::Mapper.reset(xbar_env: 'canada', app_env: 'test')
|
7
|
+
|
8
|
+
threads = []
|
9
|
+
|
10
|
+
class User < ActiveRecord::Base; end
|
11
|
+
|
12
|
+
config = XBar::Mapper.shards[:canada][0].spec.config
|
13
|
+
|
14
|
+
if config[:adapter] == "mysql2"
|
15
|
+
client = Mysql2::Client.new(config)
|
16
|
+
client.query("DELETE FROM users")
|
17
|
+
end
|
18
|
+
|
19
|
+
5.times do |i|
|
20
|
+
threads << Thread.new(i) do
|
21
|
+
XBar.using(:canada) do
|
22
|
+
100.times do |j|
|
23
|
+
name = "Thread_#{i}_#{j}"
|
24
|
+
User.create(:name => name)
|
25
|
+
User.using_any.all # allow read from slave
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
threads.each(&:join)
|
32
|
+
|
33
|
+
XBar::Mapper.disconnect_all!
|
34
|
+
|
35
|
+
if config[:adapter] == "mysql2"
|
36
|
+
results = client.query("SELECT COUNT(*) AS count FROM users")
|
37
|
+
results.each do |row|
|
38
|
+
puts row["count"]
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
puts User.using(:canada).all.size
|
44
|
+
puts User.using(:canada).all.size
|
45
|
+
puts User.using(:canada_east).all.size
|
46
|
+
puts User.using(:canada_central).all.size
|
47
|
+
puts User.using(:canada_west).all.size
|
data/examples/pause.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative "lib/helpers"
|
2
|
+
|
3
|
+
include XBar::Example::Helpers
|
4
|
+
|
5
|
+
# More setup, before we start up threads.
|
6
|
+
XBar.directory = File.expand_path(File.dirname(__FILE__))
|
7
|
+
XBar::Mapper.reset(xbar_env: 'canada', app_env: 'test')
|
8
|
+
class User < ActiveRecord::Base; end
|
9
|
+
# %x{ ssh _mysql@deimos repctl switch_master 1 2 3 }
|
10
|
+
|
11
|
+
empty_users_table(:canada)
|
12
|
+
|
13
|
+
puts %x{ ssh _mysql@deimos repctl status}
|
14
|
+
|
15
|
+
do_work(5, 100, :canada)
|
16
|
+
|
17
|
+
sleep 1
|
18
|
+
puts "Requesting all proxies to pause"
|
19
|
+
request_pause
|
20
|
+
print "Requests complete, waiting for pause..."
|
21
|
+
wait_for_pause
|
22
|
+
puts("done")
|
23
|
+
|
24
|
+
count = query_users_table(:canada)
|
25
|
+
puts "Before pause 1: entered #{count} records in master replica of Canada shard"
|
26
|
+
sleep 1
|
27
|
+
count = query_users_table(:canada)
|
28
|
+
puts "Before pause 2: entered #{count} records in master replica of Canada shard"
|
29
|
+
|
30
|
+
unpause
|
31
|
+
|
32
|
+
join_workers
|
33
|
+
|
34
|
+
cleanup_exited_threads
|
35
|
+
|
36
|
+
count = query_users_table(:canada)
|
37
|
+
puts "After join: #{count} records in master replica of Canada shard"
|
38
|
+
|
39
|
+
puts "Done"
|
40
|
+
exit 0
|
41
|
+
|
42
|
+
puts %x{ ssh _mysql@deimos repctl status}
|
43
|
+
|
44
|
+
puts %x{ ssh _mysql@deimos repctl switch_master 2 1 3 }
|
45
|
+
puts %x{ ssh _mysql@deimos repctl status }
|
46
|
+
|
47
|
+
XBar::Mapper.reset(xbar_env: 'canada2', app_env: 'test')
|
48
|
+
|
49
|
+
do_work(5, 10, :canada)
|
50
|
+
|
51
|
+
join_workers
|
52
|
+
User.using(:canada_central).all.size
|
53
|
+
cleanup_exited_threads
|
54
|
+
|
55
|
+
puts %x{ ssh _mysql@deimos repctl status}
|
56
|
+
|
57
|
+
puts query_users_table(:canada)
|
58
|
+
puts User.using(:canada).all.size
|
59
|
+
puts User.using(:canada_east).all.size
|
60
|
+
puts User.using(:canada_central).all.size
|
61
|
+
puts User.using(:canada_west).all.size
|
62
|
+
|
63
|
+
# Switch master back to server 1 for the benefit of
|
64
|
+
# other tests.
|
65
|
+
puts %x{ ssh _mysql@deimos repctl switch_master 1 2 3 }
|
66
|
+
puts %x{ ssh _mysql@deimos repctl status }
|
67
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative "lib/helpers"
|
2
|
+
|
3
|
+
include XBar::Example::Helpers
|
4
|
+
|
5
|
+
# More setup, before we start up threads.
|
6
|
+
XBar.directory = File.expand_path(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
puts "Using XBar config files from #{XBar.directory}/config"
|
9
|
+
|
10
|
+
XBar::Mapper.reset(xbar_env: 'canada', app_env: 'test')
|
11
|
+
class User < ActiveRecord::Base; end
|
12
|
+
%x{ ssh _mysql@deimos repctl switch_master 1 2 3 }
|
13
|
+
|
14
|
+
empty_users_table(:canada)
|
15
|
+
|
16
|
+
puts %x{ ssh _mysql@deimos repctl status}
|
17
|
+
|
18
|
+
do_work(5, 100, :canada)
|
19
|
+
|
20
|
+
sleep 1
|
21
|
+
puts "Requesting all proxies to pause"
|
22
|
+
request_pause
|
23
|
+
print "Requests complete, waiting for pause..."
|
24
|
+
wait_for_pause
|
25
|
+
puts("done")
|
26
|
+
|
27
|
+
count = query_users_table(:canada)
|
28
|
+
puts "After pause : entered #{count} records in master replica of Canada shard"
|
29
|
+
|
30
|
+
print "Switching master..."
|
31
|
+
puts %x{ ssh _mysql@deimos repctl switch_master 2 1 3 }
|
32
|
+
print "done:"
|
33
|
+
puts %x{ ssh _mysql@deimos repctl status }
|
34
|
+
|
35
|
+
print "Switching to new XBar environment..."
|
36
|
+
XBar::Mapper.reset(xbar_env: 'canada2', app_env: 'test')
|
37
|
+
puts "done."
|
38
|
+
|
39
|
+
print "Resuming paused threads..."
|
40
|
+
unpause
|
41
|
+
puts "done."
|
42
|
+
|
43
|
+
print "Waiting for all workers to complete..."
|
44
|
+
join_workers
|
45
|
+
puts "done"
|
46
|
+
|
47
|
+
cleanup_exited_threads
|
48
|
+
|
49
|
+
count = query_users_table(:canada)
|
50
|
+
puts %x{ ssh _mysql@deimos repctl status}
|
51
|
+
|
52
|
+
puts query_users_table(:canada)
|
53
|
+
puts User.using(:canada).all.size
|
54
|
+
puts User.using(:canada_east).all.size
|
55
|
+
puts User.using(:canada_central).all.size
|
56
|
+
puts User.using(:canada_west).all.size
|
57
|
+
|
58
|
+
# Switch master back to server 1 for the benefit of
|
59
|
+
# other tests.
|
60
|
+
puts %x{ ssh _mysql@deimos repctl switch_master 1 2 3 }
|
61
|
+
puts %x{ ssh _mysql@deimos repctl status }
|
data/examples/switch.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative "lib/helpers"
|
2
|
+
|
3
|
+
include XBar::Example::Helpers
|
4
|
+
|
5
|
+
# More setup, before we start up threads.
|
6
|
+
XBar.directory = File.expand_path(File.dirname(__FILE__))
|
7
|
+
XBar::Mapper.reset(xbar_env: 'canada', app_env: 'test')
|
8
|
+
class User < ActiveRecord::Base; end
|
9
|
+
%x{ ssh _mysql@deimos repctl switch_master 1 2 3 }
|
10
|
+
empty_users_table(:canada)
|
11
|
+
|
12
|
+
puts %x{ ssh _mysql@deimos repctl status}
|
13
|
+
|
14
|
+
do_work(5, 10, :canada)
|
15
|
+
join_workers
|
16
|
+
cleanup_exited_threads
|
17
|
+
|
18
|
+
puts %x{ ssh _mysql@deimos repctl status}
|
19
|
+
|
20
|
+
puts %x{ ssh _mysql@deimos repctl switch_master 2 1 3 }
|
21
|
+
puts %x{ ssh _mysql@deimos repctl status }
|
22
|
+
|
23
|
+
XBar::Mapper.reset(xbar_env: 'canada2', app_env: 'test')
|
24
|
+
|
25
|
+
do_work(5, 10, :canada)
|
26
|
+
|
27
|
+
join_workers
|
28
|
+
User.using(:canada_central).all.size
|
29
|
+
cleanup_exited_threads
|
30
|
+
|
31
|
+
puts %x{ ssh _mysql@deimos repctl status}
|
32
|
+
|
33
|
+
puts query_users_table(:canada)
|
34
|
+
puts User.using(:canada).all.size
|
35
|
+
puts User.using(:canada_east).all.size
|
36
|
+
puts User.using(:canada_central).all.size
|
37
|
+
puts User.using(:canada_west).all.size
|
38
|
+
|
39
|
+
# Switch master back to server 1 for the benefit of
|
40
|
+
# other tests.
|
41
|
+
puts %x{ ssh _mysql@deimos repctl switch_master 1 2 3 }
|
42
|
+
puts %x{ ssh _mysql@deimos repctl status }
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
|
-
remote: /
|
2
|
+
remote: /opt/projects/xbar
|
3
3
|
specs:
|
4
|
-
xbar (0.4.
|
4
|
+
xbar (0.4.2)
|
5
5
|
activerecord
|
6
6
|
mysql2
|
7
7
|
|
@@ -53,7 +53,7 @@ GEM
|
|
53
53
|
mime-types (1.17.2)
|
54
54
|
multi_json (1.0.4)
|
55
55
|
mysql2 (0.3.11)
|
56
|
-
pg (0.
|
56
|
+
pg (0.13.1)
|
57
57
|
polyglot (0.3.3)
|
58
58
|
rack (1.4.1)
|
59
59
|
rack-cache (1.1)
|
data/lib/xbar.rb
CHANGED
data/lib/xbar/mapper.rb
CHANGED
@@ -58,7 +58,7 @@ module XBar
|
|
58
58
|
@@cached_config = nil
|
59
59
|
@@shards = HashWithIndifferentAccess.new
|
60
60
|
@@connections = HashWithIndifferentAccess.new
|
61
|
-
@@proxies =
|
61
|
+
@@proxies = {}
|
62
62
|
@@adapters = Set.new
|
63
63
|
@@config = nil
|
64
64
|
@@app_env = nil
|
@@ -76,10 +76,17 @@ module XBar
|
|
76
76
|
|
77
77
|
def config_from_file
|
78
78
|
file_name = config_file_name
|
79
|
-
|
79
|
+
|
80
80
|
if File.exists? file_name
|
81
|
+
if XBar.debug
|
82
|
+
puts "XBar::Mapper, reading configuration from file #{file_name}"
|
83
|
+
end
|
81
84
|
config = JSON.parse(ERB.new(File.read(file_name)).result)
|
82
85
|
else
|
86
|
+
if XBar.debug
|
87
|
+
puts("XBar::Mapper: No config file #{file_name} -- " +
|
88
|
+
"Deriving defaults.")
|
89
|
+
end
|
83
90
|
config = {}
|
84
91
|
end
|
85
92
|
HashWithIndifferentAccess.new(config)
|
@@ -115,6 +122,7 @@ module XBar
|
|
115
122
|
# a hash in the XBar module.
|
116
123
|
#
|
117
124
|
def reset(options = {})
|
125
|
+
force = options.delete(:force)
|
118
126
|
new_xbar_env = options[:xbar_env] || xbar_env
|
119
127
|
if (new_xbar_env != xbar_env) || (options[:clear_cache]) ||
|
120
128
|
(!@@cached_config.nil? && @@cached_config.empty?)
|
@@ -123,7 +131,9 @@ module XBar
|
|
123
131
|
self.xbar_env = new_xbar_env
|
124
132
|
self.app_env = options[:app_env] if options[:app_env]
|
125
133
|
|
126
|
-
|
134
|
+
if XBar.debug
|
135
|
+
puts "XBar::Mapper#reset, xbar_env=#{xbar_env}, app_env=#{app_env}"
|
136
|
+
end
|
127
137
|
initialize_shards(config)
|
128
138
|
initialize_options(config)
|
129
139
|
|
@@ -159,9 +169,14 @@ module XBar
|
|
159
169
|
@@adapters << connection_pool.spec.config
|
160
170
|
end
|
161
171
|
|
162
|
-
@@proxies.each do |proxy|
|
163
|
-
|
172
|
+
@@proxies.values.each do |proxy|
|
173
|
+
if force
|
174
|
+
proxy.do_reset
|
175
|
+
else
|
176
|
+
proxy.request_reset
|
177
|
+
end
|
164
178
|
end
|
179
|
+
|
165
180
|
self
|
166
181
|
end
|
167
182
|
|
@@ -174,12 +189,33 @@ module XBar
|
|
174
189
|
@@options[:verify_connection] ||= false
|
175
190
|
end
|
176
191
|
|
192
|
+
# Register a proxy on behalf of the current thread. This is only
|
193
|
+
# called by Proxy#new. It is up to the calling thread to assign
|
194
|
+
# Thread.current[:connection_proxy] = <new proxy> if it wishes.
|
177
195
|
def register(proxy)
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
196
|
+
reset if shards.empty?
|
197
|
+
if @@proxies[Thread.current.object_id]
|
198
|
+
raise RuntimeError, "Mapper: already registered this proxy"
|
199
|
+
end
|
200
|
+
@@proxies[Thread.current.object_id] = proxy
|
201
|
+
end
|
202
|
+
|
203
|
+
# Unregister the proxy for the current thread, or for the specified
|
204
|
+
# thread. A thread can be specified by passing a Thread instance or
|
205
|
+
# its object_id.
|
206
|
+
def unregister(thread_spec = Thread.current)
|
207
|
+
thread_spec = thread_spec.object_id if thread_spec.instance_of?(Thread)
|
208
|
+
XBar::Mapper.proxies.delete(thread_spec)
|
209
|
+
end
|
210
|
+
|
211
|
+
def disconnect_all!
|
212
|
+
shards.each do |name, pool_list|
|
213
|
+
pool_list.each_with_index do |p, i|
|
214
|
+
if p.connected?
|
215
|
+
p.disconnect!
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
183
219
|
end
|
184
220
|
|
185
221
|
def app_env
|
@@ -207,9 +243,15 @@ module XBar
|
|
207
243
|
|
208
244
|
def initialize_shards(aconfig)
|
209
245
|
|
246
|
+
# The way this works right now, if the same adapter spec is used
|
247
|
+
# in multiple shards, we will get multiple connection pools
|
248
|
+
# initialized with the same spec. It might be better to share
|
249
|
+
# connection pools among the shards.
|
250
|
+
|
210
251
|
@@connections.clear
|
211
252
|
@@adapters.clear
|
212
253
|
@@shards.clear
|
254
|
+
pool_for_spec = {}
|
213
255
|
|
214
256
|
if aconfig
|
215
257
|
begin
|
@@ -227,18 +269,23 @@ module XBar
|
|
227
269
|
end
|
228
270
|
if connection_key.kind_of? String
|
229
271
|
spec = aconfig["connections"][connection_key]
|
230
|
-
|
272
|
+
# unless pool = pool_for_spec[spec]
|
273
|
+
pool = install_connection(connection_key, spec)
|
274
|
+
# pool_for_spec[spec] = pool
|
275
|
+
# end
|
231
276
|
@@shards[shard_key] = [pool]
|
232
277
|
else # an array of connection keys
|
233
278
|
@@shards[shard_key] = []
|
234
279
|
connection_key.each do |conn_key|
|
235
280
|
spec = aconfig["connections"][conn_key]
|
236
|
-
pool =
|
281
|
+
# unless pool = pool_for_spec[spec]
|
282
|
+
pool = install_connection(conn_key, spec)
|
283
|
+
# pool_for_spec[spec] = pool
|
284
|
+
# end
|
237
285
|
@@shards[shard_key] << pool
|
238
286
|
end
|
239
287
|
end
|
240
288
|
end
|
241
|
-
# @@shards[:master] = [@@connections[:master]]
|
242
289
|
end
|
243
290
|
|
244
291
|
# Should return a ConnectionPool.
|
@@ -285,7 +332,7 @@ module XBar
|
|
285
332
|
end
|
286
333
|
|
287
334
|
# Give module XBar::Mapper the above class methods.
|
288
|
-
|
335
|
+
self.extend(ClassMethods)
|
289
336
|
|
290
337
|
Mapper.exports.each do |meth|
|
291
338
|
define_method(meth) {Mapper.send(meth)}
|