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.
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)}