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
data/lib/xbar/model.rb CHANGED
@@ -49,16 +49,11 @@ module XBar::Model
49
49
  if new_record? || connection_proxy.in_block_scope?
50
50
  if XBar.debug
51
51
  type = new_record? ? "New" : "Existing"
52
- puts "#{type} model callback, current_shard=#{connection_proxy.current_shard}, " +
53
- "block_scope=#{connection_proxy.in_block_scope?}"
54
52
  end
55
53
  self.current_shard = connection_proxy.current_shard
56
54
  else
57
55
  if XBar.debug
58
56
  type = new_record? ? "New" : "Existing"
59
- puts "#{type} model callback, current_shard=#{connection_proxy.current_shard} " +
60
- "last_current_shard=#{connection_proxy.last_current_shard}, " +
61
- "block_scope=#{connection_proxy.in_block_scope?}"
62
57
  end
63
58
  self.current_shard = connection_proxy.last_current_shard
64
59
  end
@@ -78,7 +73,6 @@ module XBar::Model
78
73
  end
79
74
 
80
75
  def connection_proxy
81
- puts "Model allocating new connection proxy" unless Thread.current[:connection_proxy]
82
76
  Thread.current[:connection_proxy] ||= XBar::Proxy.new
83
77
  end
84
78
 
@@ -86,10 +80,7 @@ module XBar::Model
86
80
  if should_use_normal_connection?
87
81
  connection_without_xbar
88
82
  else
89
- #puts "Model connection with octopus" if XBar.debug
90
- #if (connection_proxy.current_model.nil?) || (self != ActiveRecord::Base)
91
- connection_proxy.current_model = self
92
- #end
83
+ connection_proxy.current_model = self
93
84
  connection_proxy
94
85
  end
95
86
  end
data/lib/xbar/proxy.rb CHANGED
@@ -17,27 +17,30 @@ class XBar::Proxy
17
17
  # Setters for these are written by hand below.
18
18
  attr_reader :current_model, :current_shard
19
19
 
20
- attr_reader :shard_list
20
+ attr_reader :shard_list, :adapters
21
21
 
22
22
  attr_accessor :slave_read_allowed
23
23
 
24
24
  def initialize
25
- puts "Initializing new proxy."
25
+ if XBar.debug
26
+ puts "Proxy##{ BLUE_TEXT}{initialize}#{RESET_COLORS}: Initializing new proxy."
27
+ end
28
+ @reset = false
29
+ @pause = false
26
30
  register
27
31
  reset_shards
28
32
  clean_proxy
33
+ @adapters = XBar::Mapper.adapters
34
+ @mylock = Mutex.new
35
+ @pause_lock = Mutex.new
36
+ @pause_cv = ConditionVariable.new
37
+ @paused = false
29
38
  end
30
-
31
- def reset_proxy
32
- @current_shard = :master
33
- puts ">>> reset_proxy: #{current_shard}" if XBar.debug
34
- clear_block_scope
35
- reset_shards
36
- end
37
-
39
+
40
+ # Called from migration.
38
41
  def clean_proxy
39
42
  if XBar.debug
40
- puts "Proxy##{BLUE_TEXT}clean_proxy=#{RESET_COLORS}: " +
43
+ puts "Proxy##{BLUE_TEXT}clean_proxy:#{RESET_COLORS}: " +
41
44
  "current shard = #{@current_shard}"
42
45
  end
43
46
  @current_shard = :master
@@ -45,12 +48,11 @@ class XBar::Proxy
45
48
  end
46
49
 
47
50
  def current_shard=(shard_name)
48
- # shard name might actually be a list of shard names in the
49
- # case of migration. Make it an array in all cases to check it.
51
+ check_for_reset
52
+ # The shard hard name might actually be a list of shard names in
53
+ # the case of migration. Make it an array in all cases to check it.
50
54
  Array(shard_name).each do |s|
51
55
  if !@shard_list.member? s
52
- puts "<<< #{@shard_list.keys} >>>"
53
- puts "<<< #{XBar::Mapper.shards.keys} >>>"
54
56
  raise "Nonexistent Shard Name: #{s}"
55
57
  end
56
58
  end
@@ -61,21 +63,6 @@ class XBar::Proxy
61
63
  @current_shard = shard_name
62
64
  end
63
65
 
64
- def select_shard
65
- if current_shard.kind_of? Array
66
- shard = current_shard.first
67
- if current_shard.size != 1
68
- puts "WARNING: selecting only first shard from array"
69
- end
70
- else
71
- shard = current_shard
72
- end
73
- unless @shard_list[shard]
74
- puts "Shard not found: current_shard = #{shard}, @shard_list = #{@shard_list.keys}"
75
- end
76
- @shard_list[shard]
77
- end
78
-
79
66
  def current_model=(model)
80
67
  # The way that this function is used internally, kind_of?(ActiveRecord::Base)
81
68
  # is always false -- we're always passing the class.cur
@@ -104,7 +91,7 @@ class XBar::Proxy
104
91
  end
105
92
 
106
93
  def should_clean_table_name?
107
- adapters.size > 1
94
+ @adapters.size > 1
108
95
  end
109
96
 
110
97
  def verify_connection
@@ -112,6 +99,8 @@ class XBar::Proxy
112
99
  end
113
100
 
114
101
  def run_queries_on_shard(shard_name, use_scope = true)
102
+ check_for_reset
103
+ check_for_pause
115
104
  older_shard = current_shard
116
105
  enter_block_scope if use_scope
117
106
  self.current_shard = shard_name
@@ -142,6 +131,8 @@ class XBar::Proxy
142
131
  end
143
132
 
144
133
  def transaction(options = {}, &block)
134
+ check_for_reset
135
+ check_for_pause
145
136
  select_shard.transaction(options, &block)
146
137
  end
147
138
 
@@ -153,6 +144,11 @@ class XBar::Proxy
153
144
  select_shard.quote_table_name(table_name)
154
145
  end
155
146
 
147
+ def clear_cache!
148
+ check_for_reset
149
+ select_shard.run_queries(:clear_cache!)
150
+ end
151
+
156
152
  def method_missing(method, *args, &block)
157
153
  if XBar.debug
158
154
  puts("\nProxy##{BLUE_TEXT}method_missing#{RESET_COLORS}: " +
@@ -160,7 +156,6 @@ class XBar::Proxy
160
156
  "current_shard=#{current_shard}, " +
161
157
  "in_block_scope=#{in_block_scope?}")
162
158
  end
163
-
164
159
  if method.to_s =~ /insert|select|execute/ && !in_block_scope? # should clean connection
165
160
  shard = @last_current_shard = current_shard
166
161
  clean_proxy
@@ -175,7 +170,9 @@ class XBar::Proxy
175
170
  end
176
171
 
177
172
  def connection_pool
178
- cp = shards[current_shard].first
173
+ # Or we could make a case for selecting master replica from
174
+ # the master shard, rather than the current shard. XXX.
175
+ cp = select_shard.master
179
176
  cp.automatic_reconnect = true if XBar.rails31?
180
177
  cp
181
178
  end
@@ -220,6 +217,61 @@ class XBar::Proxy
220
217
  end
221
218
  end
222
219
 
220
+ def request_pause
221
+ @pause_lock.synchronize do
222
+ @pause = true
223
+ end
224
+ end
225
+
226
+ def paused?
227
+ @pause
228
+ end
229
+
230
+ def check_for_pause
231
+ if @pause && (open_transactions == 0)
232
+ @pause_lock.synchronize do
233
+ @pause = true
234
+ @pause_cv.wait(@pause_lock)
235
+ @pause = false
236
+ end
237
+ end
238
+ end
239
+
240
+ def unpause
241
+ @pause_cv.signal
242
+ end
243
+
244
+ def request_reset(opts = {})
245
+ @mylock.synchronize do
246
+ @reset = true
247
+ if opts[:hard_reset]
248
+ @hard_reset = true
249
+ end
250
+ end
251
+ end
252
+
253
+ def reset_complete?
254
+ @mylock.synchronize do
255
+ @reset == false
256
+ end
257
+ end
258
+
259
+ def do_reset
260
+ reset_shards
261
+ @adapters = adapters
262
+ @mylock.synchronize do
263
+ @reset = false
264
+ clean_proxy if @hard_reset
265
+ @hard_reset = false
266
+ end
267
+ end
268
+
269
+ def check_for_reset
270
+ if @reset && (open_transactions == 0)
271
+ do_reset
272
+ end
273
+ end
274
+
223
275
  private
224
276
 
225
277
  def reset_shards
@@ -234,7 +286,26 @@ class XBar::Proxy
234
286
  slaves = replicas[1..-1] # cdr, could be empty array
235
287
  @shard_list[shard_name] = XBar::Shard.new(self, shard_name, master, slaves)
236
288
  end
289
+ end
290
+
291
+ def open_transactions
292
+ # The shards as known to the mapper have already changed.
293
+ @shard_list.values.inject(0) {|sum, shard| sum = shard.open_transactions}
294
+ end
237
295
 
296
+ def select_shard
297
+ if current_shard.kind_of? Array
298
+ shard = current_shard.first
299
+ if current_shard.size != 1
300
+ puts "WARNING: selecting only first shard from array"
301
+ end
302
+ else
303
+ shard = current_shard
304
+ end
305
+ unless @shard_list[shard]
306
+ puts "Shard not found: current_shard = #{shard}, @shard_list = #{@shard_list.keys}"
307
+ end
308
+ @shard_list[shard]
238
309
  end
239
310
 
240
311
  def insert_some_sql(conn, data)
data/lib/xbar/shard.rb CHANGED
@@ -58,6 +58,22 @@ module XBar
58
58
  master.connection.quote_table_name(table_name)
59
59
  end
60
60
 
61
+ def open_transactions
62
+ tr_count = 0
63
+ @master.connections.inject(0) {|s, c| s + c.open_transactions}
64
+ @master.connections.each do |c|
65
+ val = c.instance_variable_get(:@_current_transaction_records) # nil or array
66
+ tr_count += val.size if val
67
+ end
68
+ @slaves.each do |s|
69
+ s.connections.each do |c|
70
+ val = c.instance_variable_get(:@_current_transaction_records) # nil or array
71
+ tr_count += val.size if val
72
+ end
73
+ end
74
+ tr_count
75
+ end
76
+
61
77
  private
62
78
 
63
79
  def prepare_connection_pool(pool)
@@ -96,6 +112,6 @@ module XBar
96
112
  end
97
113
  run_queries_on_replica(replica, method, *args, &block) # return sql
98
114
  end
99
-
115
+
100
116
  end
101
117
  end
data/lib/xbar/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module XBar
2
- VERSION = '0.4.1'
2
+ VERSION = '0.4.2'
3
3
  end
data/run CHANGED
@@ -1,7 +1,5 @@
1
1
  #!/bin/bash
2
- #RUBY=/usr/local/rvm/rubies/ruby-1.9.3-p0/bin/ruby
3
- RDEBUG=/usr/local/rvm/gems/ruby-1.9.3-p0@edge/bin/rdebug
4
- RSPEC=/usr/local/rvm/gems/ruby-1.9.3-p0@edge/bin/rspec
2
+
5
3
  SPEC_DIR="spec/xbar"
6
4
 
7
5
  export BUNDLE_GEMFILE=/Volumes/Opt/projects/xbar/gemfiles/rails32.gemfile
@@ -11,9 +9,14 @@ LINE=$2
11
9
 
12
10
  if [ -n "$SPEC" ]
13
11
  then
14
- TEST="$SPEC_DIR/${SPEC}_spec.rb"
12
+ if [ "$SPEC" = "all" ]
13
+ then
14
+ TEST="all"
15
+ else
16
+ TEST="$SPEC_DIR/${SPEC}_spec.rb"
17
+ fi
15
18
  else
16
- echo "run.sh spec [line]"
19
+ echo "run.sh spec [line] | all"
17
20
  exit 1
18
21
  fi
19
22
 
@@ -22,6 +25,11 @@ then
22
25
  TEST="${TEST}:${LINE}"
23
26
  fi
24
27
 
25
- echo $TEST
28
+ if [ "$SPEC" == "all" ]
29
+ then
30
+ bundle exec ruby -S rspec -fd -b -c ./spec/xbar/*_spec.rb
31
+ else
32
+ bundle exec ruby -S rspec -fd -b -c ${TEST}
33
+ fi
34
+
26
35
 
27
- bundle exec ${RUBY} ${RSPEC} -fd -b -c ${TEST}
data/spec/console.rb CHANGED
@@ -4,7 +4,6 @@
4
4
 
5
5
  require 'rspec'
6
6
  require 'spec_helper'
7
- require 'support/database_models'
8
7
 
9
8
  module XBar
10
9
  def self.directory
data/spec/spec_helper.rb CHANGED
@@ -5,15 +5,18 @@ require "active_record"
5
5
  require "action_controller"
6
6
  require "xbar"
7
7
  require "support/xbar_helper"
8
- require "support/database_models"
9
8
 
10
9
  MIGRATIONS_ROOT = File.expand_path(File.join(File.dirname(__FILE__), 'migrations'))
11
10
 
12
- XBar.directory = File.expand_path("../../spec", __FILE__)
11
+ XBar.directory = File.expand_path(File.dirname(__FILE__))
12
+
13
+ # Must be after setting the XBar directory.
14
+ require "support/database_models"
13
15
 
14
16
  RSpec.configure do |config|
15
17
 
16
18
  config.before(:each) do
19
+ # XBar.directory = File.expand_path(File.dirname(__FILE__))
17
20
  XBar.stub!(:directory).and_return(File.dirname(__FILE__))
18
21
  end
19
22
 
@@ -337,6 +337,7 @@ describe XBar::Association do
337
337
  role = @new_brazil_programmer.projects.create(:name => "New VB App :-/")
338
338
  @new_brazil_programmer.projects.find(:first).should == role
339
339
  @new_brazil_programmer.projects.destroy_all
340
+ sleep(0.5) # Replication delay sometimes causes a spurious failure
340
341
  @new_brazil_programmer.projects.find(:first).should be_nil
341
342
  end
342
343
 
@@ -16,7 +16,7 @@ describe XBar::Mapper do
16
16
  end
17
17
 
18
18
  it "should return all environments" do
19
- XBar::Mapper.environments.should == ["test", "development", "staging"]
19
+ XBar::Mapper.environments.should == ["test", "development", "staging", "local_test"]
20
20
  end
21
21
 
22
22
  it "should work with thinking sphinx" do
@@ -259,16 +259,14 @@ describe XBar::Mapper do
259
259
  end
260
260
 
261
261
  end
262
-
263
-
264
262
  end
265
-
266
263
  end
267
264
 
268
265
  describe "when you specify a bogus application environment" do
269
266
  before(:each) do
270
267
  set_xbar_env("acme", "bogus")
271
268
  @proxy = Thread.current[:connection_proxy]
269
+ @proxy.check_for_reset
272
270
  end
273
271
 
274
272
  it "should initialize the list of shards" do
@@ -79,14 +79,12 @@ describe XBar::Model do
79
79
  end
80
80
 
81
81
  it "should allow creating more than one user" do
82
- XBar.debug = true
83
82
  User.using(:canada).create([{ :name => 'America User 1' }, { :name => 'America User 2' }])
84
83
  User.create!(:name => "Thiago")
85
84
  User.using(:canada).find_by_name("America User 1").should_not be_nil
86
85
  User.using(:canada).find_by_name("America User 2")#.should_not be_nil
87
86
  User.using(:master).find_by_name("Thiago").should_not be_nil
88
87
  User.all.size.should == 1 # fail -- America User 2 is being created on master!!!
89
- XBar.debug = false
90
88
  end
91
89
 
92
90
  it "should work when you have a SQLite3 shard" do
@@ -340,7 +338,7 @@ describe XBar::Model do
340
338
  describe "when using a environment with a single adapter" do
341
339
  before (:each) do
342
340
  set_xbar_env('single_adapter', 'test')
343
- @proxy = XBar::Proxy.new
341
+ @proxy = Thread.current[:connection_proxy] || XBar::Proxy.new
344
342
  end
345
343
 
346
344
  it 'should_clean_table_name? should return false' do
@@ -361,8 +359,9 @@ describe XBar::Model do
361
359
  end
362
360
 
363
361
  describe "when you have joins/include" do
364
-
362
+
365
363
  before(:each) do
364
+ set_xbar_env('default', 'test')
366
365
 
367
366
  @client1 = Client.using(:brazil).create(:name => "Thiago")
368
367
 
@@ -381,7 +380,8 @@ describe XBar::Model do
381
380
  end
382
381
 
383
382
  it "should work with the rails 2.x syntax" do
384
- items = Item.using(:canada).find(:all, :joins => :client, :conditions => { :clients => { :id => @client2.id } })
383
+ items = Item.using(:canada).find(:all, :joins => :client,
384
+ :conditions => { :clients => { :id => @client2.id } })
385
385
  items.should == [@item1, @item2]
386
386
  end
387
387
 
@@ -393,7 +393,8 @@ describe XBar::Model do
393
393
  end
394
394
 
395
395
  it "should work for include also, rails 2.x syntax" do
396
- items = Item.using(:canada).find(:all, :include => :client, :conditions => { :clients => { :id => @client2.id } })
396
+ items = Item.using(:canada).find(:all, :include => :client,
397
+ :conditions => { :clients => { :id => @client2.id } })
397
398
  items.should == [@item1, @item2]
398
399
  end
399
400
 
@@ -405,13 +406,15 @@ describe XBar::Model do
405
406
  end
406
407
 
407
408
  it "should work for multiple includes, with rails 2.x syntax" do
408
- parts = Part.using(:canada).find(:all, :include => {:item => :client}, :conditions => {:clients => { :id => @client2.id}})
409
+ parts = Part.using(:canada).find(:all, :include => {:item => :client},
410
+ :conditions => {:clients => { :id => @client2.id}})
409
411
  parts.should == [@part1, @part2, @part3]
410
412
  parts.first.item.client.should == @client2
411
413
  end
412
414
 
413
415
  it "should work for multiple join, with rails 2.x syntax" do
414
- parts = Part.using(:canada).find(:all, :joins => {:item => :client}, :conditions => {:clients => { :id => @client2.id}})
416
+ parts = Part.using(:canada).find(:all, :joins => {:item => :client},
417
+ :conditions => {:clients => { :id => @client2.id}})
415
418
  parts.should == [@part1, @part2, @part3]
416
419
  parts.first.item.client.should == @client2
417
420
  end