xbar 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +2 -0
  3. data/README.mkdn +1 -1
  4. data/Rakefile +5 -2
  5. data/dummy/.gitignore +15 -0
  6. data/dummy/Gemfile +38 -0
  7. data/dummy/README.rdoc +261 -0
  8. data/dummy/Rakefile +7 -0
  9. data/dummy/app/assets/images/rails.png +0 -0
  10. data/dummy/app/assets/javascripts/application.js +15 -0
  11. data/dummy/app/assets/stylesheets/application.css +13 -0
  12. data/dummy/app/controllers/application_controller.rb +3 -0
  13. data/dummy/app/helpers/application_helper.rb +2 -0
  14. data/dummy/app/mailers/.gitkeep +0 -0
  15. data/dummy/app/models/.gitkeep +0 -0
  16. data/dummy/app/views/layouts/application.html.erb +14 -0
  17. data/dummy/config.ru +4 -0
  18. data/dummy/config/application.rb +59 -0
  19. data/dummy/config/boot.rb +6 -0
  20. data/dummy/config/database.yml +25 -0
  21. data/dummy/config/environment.rb +5 -0
  22. data/dummy/config/environments/development.rb +37 -0
  23. data/dummy/config/environments/production.rb +67 -0
  24. data/dummy/config/environments/test.rb +37 -0
  25. data/dummy/config/initializers/backtrace_silencers.rb +7 -0
  26. data/dummy/config/initializers/inflections.rb +15 -0
  27. data/dummy/config/initializers/mime_types.rb +5 -0
  28. data/dummy/config/initializers/secret_token.rb +7 -0
  29. data/dummy/config/initializers/session_store.rb +8 -0
  30. data/dummy/config/initializers/wrap_parameters.rb +14 -0
  31. data/dummy/config/locales/en.yml +5 -0
  32. data/dummy/config/routes.rb +58 -0
  33. data/dummy/db/schema.rb +16 -0
  34. data/dummy/db/seeds.rb +7 -0
  35. data/dummy/doc/README_FOR_APP +2 -0
  36. data/dummy/lib/assets/.gitkeep +0 -0
  37. data/dummy/lib/generators/initializer/USAGE +8 -0
  38. data/dummy/lib/generators/initializer/initializer_generator.rb +8 -0
  39. data/dummy/lib/generators/initializer/templates/initializer.rb +1 -0
  40. data/dummy/lib/tasks/.gitkeep +0 -0
  41. data/dummy/log/.gitkeep +0 -0
  42. data/dummy/public/404.html +26 -0
  43. data/dummy/public/422.html +26 -0
  44. data/dummy/public/500.html +25 -0
  45. data/dummy/public/favicon.ico +0 -0
  46. data/dummy/public/index.html +241 -0
  47. data/dummy/public/robots.txt +5 -0
  48. data/dummy/script/rails +6 -0
  49. data/dummy/test/fixtures/.gitkeep +0 -0
  50. data/dummy/test/functional/.gitkeep +0 -0
  51. data/dummy/test/integration/.gitkeep +0 -0
  52. data/dummy/test/performance/browsing_test.rb +12 -0
  53. data/dummy/test/test_helper.rb +13 -0
  54. data/dummy/test/unit/.gitkeep +0 -0
  55. data/dummy/vendor/assets/javascripts/.gitkeep +0 -0
  56. data/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
  57. data/dummy/vendor/plugins/.gitkeep +0 -0
  58. data/examples/README +3 -1
  59. data/examples/basic.rb +34 -0
  60. data/examples/config/canada.json +17 -0
  61. data/examples/config/canada2.json +17 -0
  62. data/examples/config/connection.rb +2 -0
  63. data/examples/config/simple.json +10 -10
  64. data/examples/lib/helpers.rb +90 -0
  65. data/examples/{setup.rb → lib/setup.rb} +14 -6
  66. data/examples/migrations/1_create_users.rb +1 -1
  67. data/examples/multithread.rb +47 -0
  68. data/examples/pause.rb +67 -0
  69. data/examples/pause_switch.rb +61 -0
  70. data/examples/switch.rb +42 -0
  71. data/gemfiles/rails32.gemfile.lock +3 -3
  72. data/lib/xbar.rb +1 -0
  73. data/lib/xbar/mapper.rb +61 -14
  74. data/lib/xbar/model.rb +1 -10
  75. data/lib/xbar/proxy.rb +104 -33
  76. data/lib/xbar/shard.rb +17 -1
  77. data/lib/xbar/version.rb +1 -1
  78. data/run +15 -7
  79. data/spec/console.rb +0 -1
  80. data/spec/spec_helper.rb +5 -2
  81. data/spec/xbar/association_spec.rb +1 -0
  82. data/spec/xbar/mapper_spec.rb +2 -4
  83. data/spec/xbar/model_spec.rb +11 -8
  84. data/spec/xbar/proxy_spec.rb +1 -1
  85. metadata +90 -28
  86. 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__), 'migrations'))
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
- # 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/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
@@ -1,5 +1,5 @@
1
1
  class CreateUsers < ActiveRecord::Migration
2
- using :france_nord, :france_central, :france_sud
2
+ using :produce, :bakery, :deli
3
3
  def change
4
4
  create_table :users do |t|
5
5
  t.string :name
@@ -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 }
@@ -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: /Volumes/Opt/projects/xbar
2
+ remote: /opt/projects/xbar
3
3
  specs:
4
- xbar (0.4.0)
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.12.2)
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
@@ -13,6 +13,7 @@ require 'active_support/hash_with_indifferent_access'
13
13
  module XBar
14
14
 
15
15
  class ConfigError < StandardError; end
16
+ class RuntimeError < StandardError; end
16
17
 
17
18
  class << self
18
19
  attr_accessor :debug
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
- puts "XBar::Mapper, reading configuration from file #{file_name}"
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
- # puts "XBar::Mapper#reset, xbar_env=#{xbar_env}, app_env=#{app_env}"
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
- proxy.reset_proxy
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
- @@proxies << proxy
179
-
180
- # If we hang on to a reference to proxies here, the proxy will
181
- # never be garbage collected, even when the thread that it was
182
- # assigned to goes away. Find a way to fix this. XXX
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
- pool = install_connection(connection_key, spec)
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 = install_connection(conn_key, spec)
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
- self.extend(ClassMethods)
335
+ self.extend(ClassMethods)
289
336
 
290
337
  Mapper.exports.each do |meth|
291
338
  define_method(meth) {Mapper.send(meth)}