switchman 0.0.1

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 (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