switchman 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +30 -0
  3. data/app/models/switchman/shard.rb +502 -0
  4. data/db/migrate/20130328212039_create_switchman_shards.rb +9 -0
  5. data/db/migrate/20130328224244_create_default_shard.rb +9 -0
  6. data/lib/switchman.rb +9 -0
  7. data/lib/switchman/active_record/abstract_adapter.rb +11 -0
  8. data/lib/switchman/active_record/association.rb +108 -0
  9. data/lib/switchman/active_record/attribute_methods.rb +104 -0
  10. data/lib/switchman/active_record/base.rb +95 -0
  11. data/lib/switchman/active_record/calculations.rb +63 -0
  12. data/lib/switchman/active_record/connection_handler.rb +147 -0
  13. data/lib/switchman/active_record/connection_pool.rb +117 -0
  14. data/lib/switchman/active_record/finder_methods.rb +25 -0
  15. data/lib/switchman/active_record/log_subscriber.rb +43 -0
  16. data/lib/switchman/active_record/postgresql_adapter.rb +13 -0
  17. data/lib/switchman/active_record/query_cache.rb +12 -0
  18. data/lib/switchman/active_record/query_methods.rb +184 -0
  19. data/lib/switchman/active_record/relation.rb +69 -0
  20. data/lib/switchman/cache_extensions.rb +12 -0
  21. data/lib/switchman/connection_pool_proxy.rb +62 -0
  22. data/lib/switchman/database_server.rb +197 -0
  23. data/lib/switchman/default_shard.rb +28 -0
  24. data/lib/switchman/engine.rb +91 -0
  25. data/lib/switchman/r_spec_helper.rb +124 -0
  26. data/lib/switchman/shackles.rb +34 -0
  27. data/lib/switchman/test_helper.rb +65 -0
  28. data/lib/switchman/version.rb +3 -0
  29. data/spec/dummy/Rakefile +7 -0
  30. data/spec/dummy/app/models/appendage.rb +24 -0
  31. data/spec/dummy/app/models/digit.rb +9 -0
  32. data/spec/dummy/app/models/feature.rb +5 -0
  33. data/spec/dummy/app/models/mirror_user.rb +5 -0
  34. data/spec/dummy/app/models/user.rb +23 -0
  35. data/spec/dummy/config.ru +4 -0
  36. data/spec/dummy/config/application.rb +59 -0
  37. data/spec/dummy/config/boot.rb +10 -0
  38. data/spec/dummy/config/database.yml +17 -0
  39. data/spec/dummy/config/database.yml.example +25 -0
  40. data/spec/dummy/config/environment.rb +5 -0
  41. data/spec/dummy/config/environments/development.rb +37 -0
  42. data/spec/dummy/config/environments/production.rb +67 -0
  43. data/spec/dummy/config/environments/test.rb +37 -0
  44. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  45. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  46. data/spec/dummy/config/initializers/session_store.rb +8 -0
  47. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  48. data/spec/dummy/config/routes.rb +8 -0
  49. data/spec/dummy/db/migrate/20130403132607_create_users.rb +10 -0
  50. data/spec/dummy/db/migrate/20130411202442_create_appendages.rb +10 -0
  51. data/spec/dummy/db/migrate/20130411202551_create_mirror_users.rb +9 -0
  52. data/spec/dummy/db/migrate/20131022202028_create_digits.rb +10 -0
  53. data/spec/dummy/db/migrate/20131206172923_create_features.rb +12 -0
  54. data/spec/dummy/db/schema.rb +57 -0
  55. data/spec/dummy/log/development.log +504 -0
  56. data/spec/dummy/log/test.log +29907 -0
  57. data/spec/dummy/script/rails +6 -0
  58. data/spec/dummy/tmp/cache/2E2/830/shard%2F2 +0 -0
  59. data/spec/dummy/tmp/cache/2E3/840/shard%2F3 +0 -0
  60. data/spec/dummy/tmp/cache/313/970/shard%2F30 +0 -0
  61. data/spec/dummy/tmp/cache/314/980/shard%2F31 +0 -0
  62. data/spec/dummy/tmp/cache/316/980/shard%2F15 +1 -0
  63. data/spec/dummy/tmp/cache/316/9D0/shard%2F60 +0 -0
  64. data/spec/dummy/tmp/cache/317/990/shard%2F16 +0 -0
  65. data/spec/dummy/tmp/cache/317/9C0/shard%2F43 +1 -0
  66. data/spec/dummy/tmp/cache/317/9E0/shard%2F61 +0 -0
  67. data/spec/dummy/tmp/cache/318/9A0/shard%2F17 +0 -0
  68. data/spec/dummy/tmp/cache/318/9D0/shard%2F44 +0 -0
  69. data/spec/dummy/tmp/cache/318/9F0/shard%2F62 +1 -0
  70. data/spec/dummy/tmp/cache/319/9E0/shard%2F45 +0 -0
  71. data/spec/dummy/tmp/cache/319/9F0/shard%2F54 +1 -0
  72. data/spec/dummy/tmp/cache/319/A10/shard%2F72 +1 -0
  73. data/spec/dummy/tmp/cache/319/A30/shard%2F90 +0 -0
  74. data/spec/dummy/tmp/cache/31B/9E0/shard%2F29 +1 -0
  75. data/spec/dummy/tmp/cache/321/AA0/shard%2F89 +0 -0
  76. data/spec/dummy/tmp/cache/322/AC0/shard%2F99 +1 -0
  77. data/spec/dummy/tmp/cache/344/D70/shard%2F103 +1 -0
  78. data/spec/dummy/tmp/cache/345/D80/shard%2F104 +0 -0
  79. data/spec/dummy/tmp/cache/345/DB0/shard%2F131 +1 -0
  80. data/spec/dummy/tmp/cache/345/DC0/shard%2F140 +0 -0
  81. data/spec/dummy/tmp/cache/346/D90/shard%2F105 +0 -0
  82. data/spec/dummy/tmp/cache/346/DB0/shard%2F123 +0 -0
  83. data/spec/dummy/tmp/cache/346/DD0/shard%2F222 +1 -0
  84. data/spec/dummy/tmp/cache/346/DE0/shard%2F150 +0 -0
  85. data/spec/dummy/tmp/cache/346/DF0/shard%2F240 +1 -0
  86. data/spec/dummy/tmp/cache/347/DA0/shard%2F106 +1 -0
  87. data/spec/dummy/tmp/cache/347/DC0/shard%2F124 +0 -0
  88. data/spec/dummy/tmp/cache/347/DC0/shard%2F205 +1 -0
  89. data/spec/dummy/tmp/cache/347/E10/shard%2F250 +1 -0
  90. data/spec/dummy/tmp/cache/348/DF0/shard%2F143 +1 -0
  91. data/spec/dummy/tmp/cache/348/DF0/shard%2F224 +1 -0
  92. data/spec/dummy/tmp/cache/348/E10/shard%2F161 +1 -0
  93. data/spec/dummy/tmp/cache/349/DD0/shard%2F117 +1 -0
  94. data/spec/dummy/tmp/cache/349/E40/shard%2F180 +1 -0
  95. data/spec/dummy/tmp/cache/34A/DF0/shard%2F127 +1 -0
  96. data/spec/dummy/tmp/cache/34A/DF0/shard%2F208 +1 -0
  97. data/spec/dummy/tmp/cache/34A/E10/shard%2F145 +1 -0
  98. data/spec/dummy/tmp/cache/34A/E60/shard%2F190 +1 -0
  99. data/spec/dummy/tmp/cache/34B/E30/shard%2F155 +1 -0
  100. data/spec/dummy/tmp/cache/34D/E30/shard%2F139 +0 -0
  101. data/spec/dummy/tmp/cache/34E/E50/shard%2F149 +0 -0
  102. data/spec/dummy/tmp/cache/353/EF0/shard%2F199 +1 -0
  103. data/spec/dummy/tmp/cache/3A4/E90/shard%2F10003 +1 -0
  104. data/spec/dummy/tmp/cache/3A5/ED0/shard%2F10031 +1 -0
  105. data/spec/dummy/tmp/cache/3A9/EF0/shard%2F10017 +1 -0
  106. data/spec/lib/active_record/association_spec.rb +305 -0
  107. data/spec/lib/active_record/attribute_methods_spec.rb +108 -0
  108. data/spec/lib/active_record/base_spec.rb +66 -0
  109. data/spec/lib/active_record/calculations_spec.rb +119 -0
  110. data/spec/lib/active_record/connection_handler_spec.rb +45 -0
  111. data/spec/lib/active_record/connection_pool_spec.rb +23 -0
  112. data/spec/lib/active_record/finder_methods_spec.rb +29 -0
  113. data/spec/lib/active_record/query_cache_spec.rb +20 -0
  114. data/spec/lib/active_record/query_methods_spec.rb +130 -0
  115. data/spec/lib/active_record/relation_spec.rb +38 -0
  116. data/spec/lib/cache_extensions_spec.rb +27 -0
  117. data/spec/lib/connection_pool_proxy_spec.rb +13 -0
  118. data/spec/lib/database_server_spec.rb +154 -0
  119. data/spec/lib/shackles_spec.rb +147 -0
  120. data/spec/models/shard_spec.rb +382 -0
  121. data/spec/spec_helper.rb +32 -0
  122. metadata +344 -0
@@ -0,0 +1,108 @@
1
+ require "spec_helper"
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ describe AttributeMethods do
6
+ include RSpecHelper
7
+
8
+ describe "ids" do
9
+ it "should return id relative to the current shard" do
10
+ user = User.create!
11
+ user.id.should < Shard::IDS_PER_SHARD
12
+ user.local_id.should < Shard::IDS_PER_SHARD
13
+ user.global_id.should > Shard::IDS_PER_SHARD
14
+
15
+ @shard1.activate do
16
+ user.id.should > Shard::IDS_PER_SHARD
17
+ user.local_id.should < Shard::IDS_PER_SHARD
18
+ user.global_id.should > Shard::IDS_PER_SHARD
19
+ end
20
+ end
21
+
22
+ it "should return foreign keys relative to the current shard" do
23
+ appendage = Appendage.create!
24
+
25
+ # bypass the setter; we're going to test it in just a minute
26
+
27
+ # local id, should stay local
28
+ appendage.original_user_id = 6
29
+ appendage.user_id.should == 6
30
+
31
+ # (incorrect) self referencing global id; should come out as local
32
+ appendage.original_user_id = Shard.current.global_id_for(6)
33
+ appendage.user_id.should == 6
34
+
35
+ # global id referencing another shard; should come out unscathed
36
+ appendage.original_user_id = @shard1.global_id_for(6)
37
+ appendage.user_id.should == @shard1.global_id_for(6)
38
+
39
+ @shard1.activate do
40
+ # local id in another shard, should be global in this shard
41
+ appendage.original_user_id = 6
42
+ appendage.user_id.should == Shard.default.global_id_for(6)
43
+
44
+ # (incorrect) self referencing global id; should come out as global in this shard
45
+ appendage.original_user_id = Shard.default.global_id_for(6)
46
+ appendage.user_id.should == Shard.default.global_id_for(6)
47
+
48
+ # global id referencing this shard; should come out as a local id in this shard
49
+ appendage.original_user_id = @shard1.global_id_for(6)
50
+ appendage.user_id.should == 6
51
+
52
+ # global id from an unrelated shard; should stay global
53
+ appendage.original_user_id = @shard2.global_id_for(6)
54
+ appendage.user_id.should == @shard2.global_id_for(6)
55
+ end
56
+
57
+ # now that we trust the getters, try the setters
58
+
59
+ # local stays local
60
+ appendage.user_id = 6
61
+ appendage.original_user_id.should == 6
62
+ appendage.user_id = '6'
63
+ appendage.original_user_id.should == 6
64
+
65
+ # (incorrect) global id to this shard, should become local
66
+ appendage.user_id = Shard.current.global_id_for(6)
67
+ appendage.original_user_id.should == 6
68
+ appendage.user_id = Shard.current.global_id_for(6).to_s
69
+ appendage.original_user_id.should == 6
70
+
71
+ # global id from another shard, should stay global
72
+ appendage.user_id = @shard1.global_id_for(6)
73
+ appendage.original_user_id.should == @shard1.global_id_for(6)
74
+ appendage.local_user_id.should == 6
75
+ appendage.user_id = @shard1.global_id_for(6).to_s
76
+ appendage.original_user_id.should == @shard1.global_id_for(6)
77
+ appendage.local_user_id.should == 6
78
+
79
+ @shard1.activate do
80
+ # local to this shard becomes global
81
+ appendage.user_id = 6
82
+ appendage.original_user_id.should == @shard1.global_id_for(6)
83
+ appendage.user_id = '6'
84
+ appendage.original_user_id.should == @shard1.global_id_for(6)
85
+
86
+ # global id from original shard, should become local
87
+ appendage.user_id = Shard.default.global_id_for(6)
88
+ appendage.original_user_id.should == 6
89
+ appendage.user_id = Shard.default.global_id_for(6).to_s
90
+ appendage.original_user_id.should == 6
91
+
92
+ # global id from this shard, should stay global
93
+ appendage.user_id = Shard.current.global_id_for(6)
94
+ appendage.original_user_id.should == @shard1.global_id_for(6)
95
+ appendage.user_id = Shard.current.global_id_for(6).to_s
96
+ appendage.original_user_id.should == @shard1.global_id_for(6)
97
+
98
+ # global id from unrelated shard, should stay global
99
+ appendage.user_id = @shard2.global_id_for(6)
100
+ appendage.original_user_id.should == @shard2.global_id_for(6)
101
+ appendage.user_id = @shard2.global_id_for(6).to_s
102
+ appendage.original_user_id.should == @shard2.global_id_for(6)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,66 @@
1
+ require "spec_helper"
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ describe Base do
6
+ include RSpecHelper
7
+
8
+ describe "to_param" do
9
+ it "should return nil if not persisted" do
10
+ user = User.new
11
+ user.to_param.should be_nil
12
+ end
13
+
14
+ it "should return local id if in the current shard" do
15
+ user = User.create!
16
+ user.to_param.should == user.local_id
17
+ @shard1.activate do
18
+ user2 = User.create!
19
+ user2.to_param.should == user2.local_id
20
+ end
21
+ end
22
+
23
+ it "should return a short form global id if not in the current shard" do
24
+ user = nil
25
+ @shard1.activate do
26
+ user = User.create!
27
+ end
28
+ @shard2.activate do
29
+ user.to_param.should == "#{@shard1.id}~#{user.local_id}"
30
+ end
31
+ end
32
+
33
+ it "should use to_param in url helpers" do
34
+ helpers = Rails.application.routes.url_helpers
35
+ user = nil
36
+ appendage = nil
37
+
38
+ @shard1.activate do
39
+ user = User.create!
40
+ appendage = Appendage.create!
41
+
42
+ helpers.user_path(user).should == "/users/#{user.local_id}"
43
+ helpers.user_appendages_path(user).should == "/users/#{user.local_id}/appendages"
44
+ helpers.user_appendage_path(user, appendage).should == "/users/#{user.local_id}/appendages/#{appendage.local_id}"
45
+ helpers.user_test1_path(user).should == "/users/#{user.local_id}"
46
+ helpers.user_test2_path(user).should == "/users/#{user.local_id}/test2"
47
+ end
48
+
49
+ @shard2.activate do
50
+ user_short_id = "#{@shard1.id}~#{user.local_id}"
51
+ appendage_short_id = "#{@shard1.id}~#{appendage.local_id}"
52
+
53
+ helpers.user_path(user).should == "/users/#{user_short_id}"
54
+ helpers.user_appendages_path(user).should == "/users/#{user_short_id}/appendages"
55
+ helpers.user_appendage_path(user, appendage).should == "/users/#{user_short_id}/appendages/#{appendage_short_id}"
56
+ helpers.user_test1_path(user).should == "/users/#{user_short_id}"
57
+ helpers.user_test2_path(user).should == "/users/#{user_short_id}/test2"
58
+
59
+ appendage2 = Appendage.create!
60
+ helpers.user_appendage_path(user, appendage2).should == "/users/#{user_short_id}/appendages/#{appendage2.local_id}"
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,119 @@
1
+ require "spec_helper"
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ describe Calculations do
6
+ include RSpecHelper
7
+
8
+ describe "#pluck" do
9
+ before do
10
+ @shard1.activate do
11
+ @user1 = User.create!(:name => "user1")
12
+ @appendage1 = @user1.appendages.create!
13
+ end
14
+ @shard2.activate do
15
+ @user2 = User.create!(:name => "user2")
16
+ @appendage2 = @user2.appendages.create!
17
+ end
18
+ end
19
+
20
+ it "should return non-id columns" do
21
+ User.where(:id => [@user1.id, @user2.id]).pluck(:name).sort.should == ["user1", "user2"]
22
+ end
23
+
24
+ it "should return primary ids relative to current shard" do
25
+ Appendage.where(:id => @appendage1).pluck(:id).should == [@appendage1.global_id]
26
+ Appendage.where(:id => @appendage2).pluck(:id).should == [@appendage2.global_id]
27
+ @shard1.activate do
28
+ Appendage.where(:id => @appendage1).pluck(:id).should == [@appendage1.local_id]
29
+ Appendage.where(:id => @appendage2).pluck(:id).should == [@appendage2.global_id]
30
+ end
31
+ @shard2.activate do
32
+ Appendage.where(:id => @appendage1).pluck(:id).should == [@appendage1.global_id]
33
+ Appendage.where(:id => @appendage2).pluck(:id).should == [@appendage2.local_id]
34
+ end
35
+ end
36
+
37
+ it "should return foreign ids relative to current shard" do
38
+ Appendage.where(:id => @appendage1).pluck(:user_id).should == [@user1.global_id]
39
+ Appendage.where(:id => @appendage2).pluck(:user_id).should == [@user2.global_id]
40
+ @shard1.activate do
41
+ Appendage.where(:id => @appendage1).pluck(:user_id).should == [@user1.local_id]
42
+ Appendage.where(:id => @appendage2).pluck(:user_id).should == [@user2.global_id]
43
+ end
44
+ @shard2.activate do
45
+ Appendage.where(:id => @appendage1).pluck(:user_id).should == [@user1.global_id]
46
+ Appendage.where(:id => @appendage2).pluck(:user_id).should == [@user2.local_id]
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#execute_simple_calculation" do
52
+ before do
53
+ @appendages = []
54
+ @shard1.activate do
55
+ @user1 = User.create!(:name => "user1")
56
+ @appendages << @user1.appendages.create!(:value => 1)
57
+ @appendages << @user1.appendages.create!(:value => 2)
58
+ end
59
+ @shard2.activate do
60
+ @user2 = User.create!(:name => "user2")
61
+ @appendages << @user2.appendages.create!(:value => 3)
62
+ @appendages << @user2.appendages.create!(:value => 4)
63
+ @appendages << @user2.appendages.create!(:value => 5)
64
+ end
65
+ end
66
+
67
+ it "should calculate average across shards" do
68
+ @user1.appendages.average(:value).should == 1.5
69
+ @shard1.activate {Appendage.average(:value)}.should == 1.5
70
+
71
+ @user2.appendages.average(:value).should == 4
72
+ @shard2.activate {Appendage.average(:value)}.should == 4
73
+
74
+ Appendage.where(:id => @appendages).average(:value).should == 3
75
+ end
76
+
77
+ it "should count across shards" do
78
+ @user1.appendages.count.should == 2
79
+ @shard1.activate {Appendage.count}.should == 2
80
+
81
+ @user2.appendages.count.should == 3
82
+ @shard2.activate {Appendage.count}.should == 3
83
+
84
+ Appendage.where(:id => @appendages).count.should == 5
85
+ end
86
+
87
+ it "should calculate minimum across shards" do
88
+ @user1.appendages.minimum(:value).should == 1
89
+ @shard1.activate {Appendage.minimum(:value)}.should == 1
90
+
91
+ @user2.appendages.minimum(:value).should == 3
92
+ @shard2.activate {Appendage.minimum(:value)}.should == 3
93
+
94
+ Appendage.where(:id => @appendages).minimum(:value).should == 1
95
+ end
96
+
97
+ it "should calculate maximum across shards" do
98
+ @user1.appendages.maximum(:value).should == 2
99
+ @shard1.activate {Appendage.maximum(:value)}.should == 2
100
+
101
+ @user2.appendages.maximum(:value).should == 5
102
+ @shard2.activate {Appendage.maximum(:value)}.should == 5
103
+
104
+ Appendage.where(:id => @appendages).maximum(:value).should == 5
105
+ end
106
+
107
+ it "should calculate sum across shards" do
108
+ @user1.appendages.sum(:value).should == 3
109
+ @shard1.activate {Appendage.sum(:value)}.should == 3
110
+
111
+ @user2.appendages.sum(:value).should == 12
112
+ @shard2.activate {Appendage.sum(:value)}.should == 12
113
+
114
+ Appendage.where(:id => @appendages).sum(:value).should == 15
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,45 @@
1
+ require "spec_helper"
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ describe ConnectionHandler do
6
+ include RSpecHelper
7
+
8
+ it "should use different proxies for different categories" do
9
+ Shard.connection_pool.should_not == User.connection_pool
10
+ end
11
+
12
+ it "should share underlying pools for different categories on the same shard" do
13
+ Shard.connection_pool.current_pool.should == User.connection_pool.current_pool
14
+ end
15
+
16
+ it "should insert sharding for connections established after initialization" do
17
+ User.connection_pool.should == ::ActiveRecord::Base.connection_pool
18
+ begin
19
+ config = { :adapter => 'sqlite3', :database => ':memory:', :something_unique_in_the_spec => true }
20
+ User.establish_connection(config)
21
+ User.connection_pool.should_not == ::ActiveRecord::Base.connection_pool
22
+ User.connection_pool.spec.config.should == config
23
+ User.connection_pool.should be_is_a(ConnectionPoolProxy)
24
+ @shard2.activate do
25
+ User.connection_pool.spec.config.should == ::ActiveRecord::Base.connection_pool.spec.config
26
+ User.connection_pool.spec.config.should_not == config
27
+ end
28
+ ensure
29
+ User.remove_connection
30
+ User.connection_pool.should == ::ActiveRecord::Base.connection_pool
31
+ User.connection_pool.should be_is_a(ConnectionPoolProxy)
32
+ end
33
+ end
34
+
35
+ it "should set up separate pools for different categories" do
36
+ User.connection_pool.should_not == MirrorUser.connection_pool
37
+ mu = MirrorUser.create!
38
+ MirrorUser.find(mu.local_id).should == mu
39
+ # didn't activate the :mirror_universe category
40
+ @shard1.activate { MirrorUser.find(mu.local_id).should == mu }
41
+ @shard1.activate(:mirror_universe) { MirrorUser.find_by_id(mu.local_id).should == nil }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ describe ConnectionPool do
6
+ it "should be able to access another shard on a db server after the 'primary' shard is gone" do
7
+ pending 'A "real" database"' unless Shard.default.database_server.shareable?
8
+ # separate connections
9
+ server = DatabaseServer.create(:config => Shard.default.database_server.config.dup)
10
+ s1 = server.shards.create!(:name => 'non_existent_shard') # don't actually create any schema
11
+ s2 = server.shards.create! # inherit's the default shard's config, which is functional
12
+ s1.activate do
13
+ lambda { User.count }.should raise_exception
14
+ end
15
+ # the config for s1 should not be the permanent default for all new
16
+ # connections now
17
+ s2.activate do
18
+ lambda { User.count }.should_not raise_exception
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ describe FinderMethods do
6
+ include RSpecHelper
7
+
8
+ before do
9
+ @user = @shard1.activate { User.create! }
10
+ end
11
+
12
+ describe "#find_one" do
13
+ it "should find with a global id" do
14
+ User.find(@user.global_id).should == @user
15
+ end
16
+ end
17
+
18
+ describe "#find_by_attributes" do
19
+ it "should find with a global id" do
20
+ User.find_by_id(@user.global_id).should == @user
21
+ end
22
+
23
+ it "should find with an array of global ids" do
24
+ User.find_by_id([@user.global_id]).should == @user
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ require "spec_helper"
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ describe QueryCache do
6
+ include RSpecHelper
7
+
8
+ it "should isolate queries to multiple shards on the same server" do
9
+ @shard1.activate do
10
+ User.create!
11
+ User.create!
12
+ end
13
+ @shard3.activate do
14
+ User.create!
15
+ end
16
+ @shard1.activate { User.all }.should_not == @shard3.activate { User.all }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,130 @@
1
+ require "spec_helper"
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ describe QueryMethods do
6
+ include RSpecHelper
7
+
8
+ before do
9
+ @user1 = User.create!
10
+ @appendage1 = @user1.appendages.create!
11
+ @user2 = @shard1.activate { User.create! }
12
+ @appendage2 = @user2.appendages.create!
13
+ @user3 = @shard2.activate { User.create! }
14
+ @appendage3 = @user3.appendages.create!
15
+ end
16
+
17
+ describe "#primary_shard" do
18
+ it "should be the shard if it's a shard" do
19
+ User.shard(Shard.default).primary_shard.should == Shard.default
20
+ User.shard(@shard1).primary_shard.should == @shard1
21
+ end
22
+
23
+ it "should be the first shard of an array of shards" do
24
+ User.shard([Shard.default, @shard1]).primary_shard.should == Shard.default
25
+ User.shard([@shard1, Shard.default]).primary_shard.should == @shard1
26
+ end
27
+
28
+ it "should be the object's shard if it's a model" do
29
+ User.shard(@user1).primary_shard.should == Shard.default
30
+ User.shard(@user2).primary_shard.should == @shard1
31
+ end
32
+
33
+ it "should be the default shard if it's a scope of Shard" do
34
+ User.shard(Shard.scoped).primary_shard.should == Shard.default
35
+ @shard1.activate do
36
+ User.shard(Shard.scoped).primary_shard.should == Shard.default
37
+ end
38
+ end
39
+ end
40
+
41
+ it "should default to the current shard" do
42
+ relation = User.scoped
43
+ relation.shard_value.should == Shard.default
44
+ relation.shard_source_value.should == :implicit
45
+
46
+ @shard1.activate do
47
+ relation.shard_value.should == Shard.default
48
+
49
+ relation = User.scoped
50
+ relation.shard_value.should == @shard1
51
+ relation.shard_source_value.should == :implicit
52
+ end
53
+ relation.shard_value.should == @shard1
54
+ end
55
+
56
+ describe "with primary key conditions" do
57
+ it "should be changeable, and change conditions when it is changed" do
58
+ relation = User.where(:id => @user1).shard(@shard1)
59
+ relation.shard_value.should == @shard1
60
+ relation.shard_source_value.should == :explicit
61
+ relation.where_values.first.right.should == @user1.global_id
62
+ end
63
+
64
+ it "should infer the shard from a single argument" do
65
+ relation = User.where(:id => @user2)
66
+ # execute on @shard1, with id local to that shard
67
+ relation.shard_value.should == @shard1
68
+ relation.where_values.first.right.should == @user2.local_id
69
+ end
70
+
71
+ it "should infer the shard from multiple arguments" do
72
+ relation = User.where(:id => [@user2, @user2])
73
+ # execute on @shard1, with id local to that shard
74
+ relation.shard_value.should == @shard1
75
+ relation.where_values.first.right.should == [@user2.local_id, @user2.local_id]
76
+ end
77
+
78
+ it "should infer the correct shard from an array of 1" do
79
+ relation = User.where(:id => [@user2])
80
+ # execute on @shard1, with id local to that shard
81
+ relation.shard_value.should == @shard1
82
+ relation.where_values.first.right.should == [@user2.local_id]
83
+ end
84
+
85
+ it "should do nothing when it's an array of 0" do
86
+ relation = User.where(:id => [])
87
+ # execute on @shard1, with id local to that shard
88
+ relation.shard_value.should == Shard.default
89
+ relation.where_values.first.right.should == []
90
+ end
91
+
92
+ it "should order the shards preferring the shard it already had as primary" do
93
+ relation = User.where(:id => [@user1, @user2])
94
+ relation.shard_value.should == [Shard.default, @shard1]
95
+ relation.where_values.first.right.should == [@user1.local_id, @user2.global_id]
96
+
97
+ @shard1.activate do
98
+ relation = User.where(:id => [@user1, @user2])
99
+ relation.shard_value.should == [@shard1, Shard.default]
100
+ relation.where_values.first.right.should == [@user1.global_id, @user2.local_id]
101
+ end
102
+ end
103
+ end
104
+
105
+ describe "with foreign key conditions" do
106
+ it "should be changeable, and change conditions when it is changed" do
107
+ relation = Appendage.where(:user_id => @user1)
108
+ relation.shard_value.should == Shard.default
109
+ relation.shard_source_value.should == :implicit
110
+ relation.where_values.first.right.should == @user1.local_id
111
+
112
+ relation = relation.shard(@shard1)
113
+ relation.shard_value.should == @shard1
114
+ relation.shard_source_value.should == :explicit
115
+ relation.where_values.first.right.should == @user1.global_id
116
+ end
117
+
118
+ it "should translate ids based on current shard" do
119
+ relation = Appendage.where(:user_id => [@user1, @user2])
120
+ relation.where_values.first.right.should == [@user1.local_id, @user2.global_id]
121
+
122
+ @shard1.activate do
123
+ relation = Appendage.where(:user_id => [@user1, @user2])
124
+ relation.where_values.first.right.should == [@user1.global_id, @user2.local_id]
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end